From 8fbcf7941fe5ac7b6327f7b01f78bc2cdbfe3006 Mon Sep 17 00:00:00 2001 From: Beckett Frey Date: Mon, 13 Apr 2026 16:17:07 -0500 Subject: [PATCH 1/3] refactor: remove shadowed config --- src/voxkit/config.py | 109 ------------------------------------------- 1 file changed, 109 deletions(-) delete mode 100644 src/voxkit/config.py diff --git a/src/voxkit/config.py b/src/voxkit/config.py deleted file mode 100644 index fc7214d..0000000 --- a/src/voxkit/config.py +++ /dev/null @@ -1,109 +0,0 @@ -import time -from typing import Callable, Literal - -from voxkit.services.mfa import download_acoustic_model -from voxkit.storage import models -from voxkit.storage.config import MODELS_ROOT -from voxkit.storage.utils import get_storage_root - -AppName = "VoxKit" -Dimensions = {"min_width": 200, "min_height": 800, "max_width": 500, "max_height": None} -Defaults = { - "mode": "W2TGENGINE", - "output_path": "/path/to/output", - "audio_path": "/path/to/audio", - "textgrid_path": "/path/to/textgrids", - "num_epochs": 10, -} - -Mode = Literal["MFAENGINE", "W2TGENGINE"] -HELP_URL = "https://voxkit-web.vercel.app/help" - - -def startup_routine(): - """Example startup routine to be executed on first launch.""" - print("[STARTUP] Initializing VoxKit...") - time.sleep(1) # Simulate initialization - - storage_root = get_storage_root() - print(f"[STARTUP] Storage root: {storage_root}") - - print("[STARTUP] Creating required directories...") - (storage_root / "computed-likelihoods").mkdir(parents=True, exist_ok=True) - (storage_root / "custom-likelihoods").mkdir(parents=True, exist_ok=True) - time.sleep(1) # Simulate directory setup - - # Download MFA models - print("[STARTUP] Downloading MFA models...") - mfa_models = [ - "acoustic-english_us_arpa-v3.0.0/english_us_arpa.zip", - "acoustic-spanish_mfa-v3.3.0/spanish_mfa.zip", - ] - mfa_models_path = storage_root / "MFAENGINE" / MODELS_ROOT - mfa_models_path.mkdir(parents=True, exist_ok=True) - for model in mfa_models: - success, metadata = models.create_model( - "MFAENGINE", model.split("/")[1].replace(".zip", "") - ) - if not success: - print(f"[STARTUP] Failed to create model metadata for {model}. {metadata}") - continue - model_dest = metadata.get("model_path") - if not model_dest: - print(f"[STARTUP] Model path not found in metadata for {model}.") - continue - - # Remove last part of path and relace with .zip - output_file = model_dest.parent / model.split("/")[1] - - try: - download_acoustic_model(model, str(output_file)) - # Update metadata to reflect downloaded file - print(f"[STARTUP] MFA model {model} downloaded to: {output_file}") - success, message = models.update_model_metadata( - "MFAENGINE", metadata["id"], {"model_path": str(output_file)} - ) - - if not success: - print(f"[STARTUP] Failed to update model metadata for {model}. {message}") - - print(f"[STARTUP] MFA model downloaded to: {output_file}") - except Exception as e: - print(f"[STARTUP] Failed to download MFA model {model}. Error: {e}") - - # # Download W2TG model from HuggingFace - # print("[STARTUP] Downloading W2TG model from HuggingFace...") - # # Create folder for W2TG model - # w2tg_path = storage_root / "W2TGENGINE" / MODELS_ROOT - # w2tg_path.mkdir(parents=True, exist_ok=True) - # success, metadata = models.create_model("W2TGENGINE", "prads_model") - # if not success: - # print(f"[STARTUP] Failed to create model metadata. {metadata}") - # return - # model_dest = metadata.get("model_path") - # if not model_dest: - # print("[STARTUP] Model path not found in metadata.") - # return - # result = download_and_copy_huggingface_model( - # model_path="pkadambi/Wav2TextGrid", - # destination=str(model_dest), - # ) - # if result: - # print(f"[STARTUP] W2TG model downloaded to: {result}") - # else: - # print("[STARTUP] Failed to download W2TG model.") - - try: - import nltk - - nltk.download("averaged_perceptron_tagger_eng") - - except Exception as e: - print(f"[STARTUP] Failed to download NLTK resources. Error: {e}") - - print("[STARTUP] Initialization complete!") - - -# Startup script configuration -# Set this to a callable function to run on first launch, or None to disable -STARTUP_SCRIPT: Callable[[], None] | None = startup_routine From d9b0563daf5939aeb334632f34b6bd105a0c971f Mon Sep 17 00:00:00 2001 From: Beckett Frey Date: Mon, 13 Apr 2026 16:59:05 -0500 Subject: [PATCH 2/3] replace: move to invoke for os agnostic clarity --- .github/agents/code-quality.agent.md | 20 +-- .github/workflows/code-quality.yml | 42 +++---- .github/workflows/release.yml | 2 +- .github/workflows/tests-macos.yml | 2 +- .github/workflows/tests-ubuntu.yml | 6 +- .github/workflows/tests-windows.yml | 2 +- AGENTS.md | 24 ++-- Makefile | 102 ---------------- README.md | 12 +- docs/CONTRIBUTING.md | 4 +- pyproject.toml | 1 + tasks.py | 175 +++++++++++++++++++++++++++ uv.lock | 11 ++ 13 files changed, 244 insertions(+), 159 deletions(-) delete mode 100644 Makefile create mode 100644 tasks.py diff --git a/.github/agents/code-quality.agent.md b/.github/agents/code-quality.agent.md index 0b73e0a..0becf8f 100644 --- a/.github/agents/code-quality.agent.md +++ b/.github/agents/code-quality.agent.md @@ -1,26 +1,26 @@ --- name: code-quality-agent -description: Fixes linting, formatting, and type checking issues by running make commands until all checks pass +description: Fixes linting, formatting, and type checking issues by running invoke commands until all checks pass tools: ['execute/getTerminalOutput', 'execute/runInTerminal', 'execute/runTests', 'read', 'edit', 'search'] --- # Code Quality Agent -Fix all code quality issues by running make commands iteratively and addressing any remaining problems until all checks pass. +Fix all code quality issues by running invoke commands iteratively and addressing any remaining problems until all checks pass. ## Workflow -1. Run `make format` then `make lint` (auto-fixes 60-80% of issues) -2. Run `make mypy-check` to find type errors +1. Run `invoke format` then `invoke lint` (auto-fixes 60-80% of issues) +2. Run `invoke mypy-check` to find type errors 3. Fix remaining issues manually 4. Verify: All checks must pass with exit code 0 ## Commands ```bash -make format # Auto-format with Ruff -make lint # Auto-fix linting with Ruff -make mypy-check # Type check with mypy +invoke format # Auto-format with Ruff +invoke lint # Auto-fix linting with Ruff +invoke mypy-check # Type check with mypy ``` ## Common Mypy Fixes @@ -71,7 +71,7 @@ class Child(Parent): ## Success Criteria ```bash -make format-check # No changes needed -make lint-check # No issues found -make mypy-check # No type errors +invoke format-check # No changes needed +invoke lint-check # No issues found +invoke mypy-check # No type errors ``` diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index 65a55bd..5d0d2f3 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -17,86 +17,86 @@ jobs: formatting: name: Code Formatting runs-on: ubuntu-latest - + steps: - name: Checkout code uses: actions/checkout@v4 - + - name: Set up Python 3.11 uses: actions/setup-python@v5 with: python-version: '3.11' - + - name: Install uv run: | curl -LsSf https://astral.sh/uv/install.sh | sh echo "$HOME/.cargo/bin" >> $GITHUB_PATH shell: bash - + - name: Configure Git for private repos run: | git config --global url."https://x-access-token:${{ secrets.PRIVATE_REPO_TOKEN }}@github.com/".insteadOf "https://github.com/" - + - name: Install dependencies run: uv sync - + - name: Check code formatting - run: make format-check + run: uv run invoke format-check linting: name: Linting runs-on: ubuntu-latest - + steps: - name: Checkout code uses: actions/checkout@v4 - + - name: Set up Python 3.11 uses: actions/setup-python@v5 with: python-version: '3.11' - + - name: Install uv run: | curl -LsSf https://astral.sh/uv/install.sh | sh echo "$HOME/.cargo/bin" >> $GITHUB_PATH shell: bash - + - name: Configure Git for private repos run: | git config --global url."https://x-access-token:${{ secrets.PRIVATE_REPO_TOKEN }}@github.com/".insteadOf "https://github.com/" - + - name: Install dependencies run: uv sync - + - name: Check linting - run: make lint-check + run: uv run invoke lint-check type-checking: name: Type Checking runs-on: ubuntu-latest - + steps: - name: Checkout code uses: actions/checkout@v4 - + - name: Set up Python 3.11 uses: actions/setup-python@v5 with: python-version: '3.11' - + - name: Install uv run: | curl -LsSf https://astral.sh/uv/install.sh | sh echo "$HOME/.cargo/bin" >> $GITHUB_PATH shell: bash - + - name: Configure Git for private repos run: | git config --global url."https://x-access-token:${{ secrets.PRIVATE_REPO_TOKEN }}@github.com/".insteadOf "https://github.com/" - + - name: Install dependencies run: uv sync - + - name: Check type hints - run: make mypy-check + run: uv run invoke mypy-check diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 00a5676..af00f22 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -41,7 +41,7 @@ jobs: uv sync - name: Build .app bundle - run: make build + run: uv run invoke build - name: Verify build contents run: | diff --git a/.github/workflows/tests-macos.yml b/.github/workflows/tests-macos.yml index 1f35015..d53ce2b 100644 --- a/.github/workflows/tests-macos.yml +++ b/.github/workflows/tests-macos.yml @@ -40,4 +40,4 @@ jobs: - name: Run tests run: | - make run-tests + uv run invoke run-tests diff --git a/.github/workflows/tests-ubuntu.yml b/.github/workflows/tests-ubuntu.yml index e60562c..0401d15 100644 --- a/.github/workflows/tests-ubuntu.yml +++ b/.github/workflows/tests-ubuntu.yml @@ -40,14 +40,14 @@ jobs: - name: Run tests run: | - make run-tests + uv run invoke run-tests - name: Run linting run: | - make lint-check + uv run invoke lint-check continue-on-error: true - name: Run type checking run: | - make mypy-check + uv run invoke mypy-check continue-on-error: true diff --git a/.github/workflows/tests-windows.yml b/.github/workflows/tests-windows.yml index 9fca071..1eb5895 100644 --- a/.github/workflows/tests-windows.yml +++ b/.github/workflows/tests-windows.yml @@ -40,4 +40,4 @@ jobs: - name: Run tests run: | - make run-tests + uv run invoke run-tests diff --git a/AGENTS.md b/AGENTS.md index 33b4b5d..3853fb7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -36,20 +36,20 @@ Hybrid "unstructured state + signals" PyQt pattern. See `docs/ARCHITECTURE.md` f ## Setup & Common Commands -Use `make` — do not invoke tools directly unless you need a flag `make` doesn't expose. +Use `invoke` (pyinvoke, tasks defined in `tasks.py`) — do not invoke tools directly unless you need a flag the task doesn't expose. | Command | Purpose | |---|---| -| `make setup` | Install deps + pre-commit hooks (run first) | -| `make dev` | Launch the app in dev mode | -| `make run-tests` | Unit + GUI tests | -| `make test-coverage` | Coverage for core modules | -| `make lint` / `make lint-check` | Ruff lint (fix / check) | -| `make format` / `make format-check` | Ruff format | -| `make mypy-check` | Type check | -| `make build` | Standalone executable (PyInstaller) | -| `make clean` | Remove build artifacts | -| `make help` | Full list | +| `invoke setup` | Install deps + pre-commit hooks (run first) | +| `invoke dev` | Launch the app in dev mode | +| `invoke run-tests` | Unit + GUI tests | +| `invoke test-coverage` | Coverage for core modules | +| `invoke lint` / `invoke lint-check` | Ruff lint (fix / check) | +| `invoke format` / `invoke format-check` | Ruff format | +| `invoke mypy-check` | Type check | +| `invoke build` | Standalone executable (PyInstaller) | +| `invoke clean` | Remove build artifacts | +| `invoke --list` | Full list | ## Code Standards @@ -62,7 +62,7 @@ Use `make` — do not invoke tools directly unless you need a flag `make` doesn' - Framework: `pytest`, with `pytest-qt` for GUI and `pytest-asyncio` for async. - Write tests for new business logic in `storage/`, `config/`, `analyzers/`. GUI components are excluded from coverage metrics but still testable with `pytest-qt` when useful. -- Run `make run-tests` before reporting a task complete. For UI changes, also launch `make dev` and exercise the feature — type checks don't verify user-facing behavior. +- Run `invoke run-tests` before reporting a task complete. For UI changes, also launch `invoke dev` and exercise the feature — type checks don't verify user-facing behavior. ## Commit & PR Conventions diff --git a/Makefile b/Makefile deleted file mode 100644 index 80757a4..0000000 --- a/Makefile +++ /dev/null @@ -1,102 +0,0 @@ -.PHONY: help -.DEFAULT_GOAL := help - -# Colors for output -BLUE := \033[36m -GREEN := \033[32m -YELLOW := \033[33m -RED := \033[31m -RESET := \033[0m - -help: - @echo "$(BLUE)VoxKit - Dev Commands$(RESET)" - @echo "======================================" - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "$(GREEN)%-15s$(RESET) %s\n", $$1, $$2}' - -setup: ## Install dependencies and setup pre-commit hooks - @echo "$(BLUE)Installing dependencies with uv sync...$(RESET)" - uv sync - @echo "$(BLUE)Installing pre-commit hooks...$(RESET)" - uv run pre-commit install - @echo "$(GREEN)Setup completed successfully!$(RESET)" - -watch: ## Watch for file changes and restart dev server (requires entr) - @if command -v entr >/dev/null 2>&1; then \ - echo "$(BLUE)Watching for changes... Press Ctrl+C to stop$(RESET)"; \ - find src/ -name "*.py" | entr -r uv run main.py; \ - else \ - echo "$(RED)entr not installed. Install with: brew install entr$(RESET)"; \ - echo "$(YELLOW)Falling back to single run...$(RESET)"; \ - uv run main.py; \ - fi - -dev: ## Run the development server - @echo "$(BLUE)Starting development server...$(RESET)" - uv run main.py - -build: clean ## Build standalone executable for current platform - @echo "$(BLUE)Building VoxKit for macOS...$(RESET)" - uv run --group installation python scripts/build.py build --entry main.py --name VoxKit --icon ./assets/voxkit.icns --windowed - -build-info: ## Show information about the built app - @echo "$(BLUE)Checking build output...$(RESET)" - @if [ -d "dist/VoxKit" ]; then \ - echo "$(GREEN)Found: dist/VoxKit/$(RESET)"; \ - ls -lh dist/VoxKit/; \ - echo ""; \ - echo "$(BLUE)Checking executable...$(RESET)"; \ - file dist/VoxKit/VoxKit; \ - echo ""; \ - echo "$(BLUE)Checking library dependencies...$(RESET)"; \ - otool -L dist/VoxKit/VoxKit | head -10; \ - else \ - echo "$(RED)dist/VoxKit not found. Run 'make build' first.$(RESET)"; \ - fi - -clean: ## Clean build artifacts - @echo "$(BLUE)Cleaning build artifacts...$(RESET)" - @rm -rf build/ dist/ *.spec - @find . -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true - @find . -type f -name "*.pyc" -delete 2>/dev/null || true - @echo "$(GREEN)Cleanup completed$(RESET)" - -format: ## Format code with Ruff - @echo "$(BLUE)Formatting code with Ruff...$(RESET)" - uv run --only-group dev ruff format . - -format-check: ## Check code formatting with Ruff - @echo "$(BLUE)Checking code formatting with Ruff...$(RESET)" - uv run --only-group dev ruff format --check . - -lint: ## Lint code with Ruff - @echo "$(BLUE)Linting with Ruff...$(RESET)" - uv run --only-group dev ruff check --fix . - -lint-check: ## Check linting with Ruff - @echo "$(BLUE)Checking linting with Ruff...$(RESET)" - uv run --only-group dev ruff check . - -mypy-check: ## Run mypy for type checking - @echo "$(BLUE)Running mypy for type checking...$(RESET)" - uv run --only-group dev mypy . - -fresh-slate: ## Remove virtual environment and lock file - @echo "$(BLUE)Removing virtual environment and lock file...$(RESET)" - @read -p "Are you sure you want to proceed? [y/N] " confirm && [ $${confirm} = "y" ] || [ $${confirm} = "Y" ] && rm -rf uv.lock .venv || echo "Aborted." - -run-tests: ## Run all tests (unit + GUI) - uv run pytest tests/ - -test-coverage: ## Run tests with detailed coverage report for core modules - @echo "$(BLUE)Running tests with coverage (core modules only)...$(RESET)" - uv run pytest --cov=voxkit --cov-report=term-missing --cov-report=html tests/ - @echo "$(GREEN)Coverage report generated in htmlcov/index.html$(RESET)" - -generate-coverage-badge: - @echo "$(BLUE)Generating coverage badge...$(RESET)" - uv run pytest --cov=voxkit --cov-report=xml tests/ - uv run genbadge coverage -i coverage.xml -o assets/coverage.svg - @echo "$(GREEN)Coverage badge updated: assets/coverage.svg$(RESET)" - -generate-documentation: ## Generate API documentation - uv run --group docs pdoc -o docs src/voxkit diff --git a/README.md b/README.md index 2abab69..d3318b2 100644 --- a/README.md +++ b/README.md @@ -40,9 +40,9 @@ src/voxkit/ ## Developers **Prerequisites:** -- [python](https://www.python.org/downloads/release/python-31114/) code language -- [uv](https://docs.astral.sh/uv/) package manager -- [git](https://git-scm.com/install/) version tracking +- [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) – Version control +- [uv](https://docs.astral.sh/uv/getting-started/installation/) – Python package manager +- [invoke](https://www.pyinvoke.org/installing.html) – Task runner (installed via `uv tool install invoke`) **Getting-started:** ```bash @@ -53,13 +53,13 @@ cd voxkit-desktop # As easy as... # (1) Browse developer commands -make help +invoke --list # (2) Install precommit and initialize environment -make setup +invoke setup # (3) Start app (developer mode) -make dev +invoke dev ``` --- diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index add1311..143e180 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -50,14 +50,14 @@ If you’re unsure which path applies, or you aren't part of the team on Jira, o 1. Make your changes in small, logical commits 2. Write clear, descriptive commit messages 3. **Test your changes thoroughly** - see [TESTING.md](./TESTING.md) for guidelines -4. Run `make test-coverage` to verify tests pass for core modules +4. Run `invoke test-coverage` to verify tests pass for core modules 5. Update documentation as needed ### Testing Guidelines - Write tests for new business logic in `storage/`, `config/`, and `analyzers/` modules - GUI components are excluded from coverage metrics -- Run `make test-coverage` to see coverage for testable modules +- Run `invoke test-coverage` to see coverage for testable modules - Aim for 70-80% coverage on new business logic ### Commit Message Format diff --git a/pyproject.toml b/pyproject.toml index 7722cb5..536b08e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,6 +43,7 @@ dev = [ "mypy>=1.18.2", "types-PyYAML>=6.0.0", "genbadge[coverage]>=1.1.3", + "invoke>=2.2.0", ] docs = [ "pdoc>=16.0.0", diff --git a/tasks.py b/tasks.py new file mode 100644 index 0000000..f132e3c --- /dev/null +++ b/tasks.py @@ -0,0 +1,175 @@ +"""Invoke task definitions for VoxKit. Run with `invoke ` or `invoke --list`.""" + +import shutil +import subprocess +import sys +from pathlib import Path + +from invoke import task + +BLUE = "\033[36m" +GREEN = "\033[32m" +YELLOW = "\033[33m" +RED = "\033[31m" +RESET = "\033[0m" + +ROOT = Path(__file__).parent + + +def _log(msg: str, color: str = BLUE) -> None: + print(f"{color}{msg}{RESET}") + + +def _rmtree(path: Path) -> None: + if path.is_symlink() or path.is_file(): + path.unlink(missing_ok=True) + elif path.is_dir(): + shutil.rmtree(path, ignore_errors=True) + + +@task +def setup(c): + """Install dependencies and setup pre-commit hooks.""" + _log("Installing dependencies with uv sync...") + c.run("uv sync") + _log("Installing pre-commit hooks...") + c.run("uv run pre-commit install") + _log("Setup completed successfully!", GREEN) + + +@task +def dev(c): + """Run the development server.""" + _log("Starting development server...") + c.run("uv run main.py", pty=sys.platform != "win32") + + +@task +def watch(c): + """Watch for file changes and restart dev server (requires entr on Unix).""" + if sys.platform == "win32": + _log("watch is not supported on Windows (requires entr). Falling back to dev.", YELLOW) + c.run("uv run main.py") + return + if not shutil.which("entr"): + _log("entr not installed. Install with: brew install entr (macOS) or apt install entr", RED) + _log("Falling back to single run...", YELLOW) + c.run("uv run main.py", pty=True) + return + _log("Watching for changes... Press Ctrl+C to stop") + files = [str(p) for p in (ROOT / "src").rglob("*.py")] + if not files: + _log("No Python files found under src/", RED) + return + subprocess.run( # noqa: S603 + ["entr", "-r", "uv", "run", "main.py"], # noqa: S607 + input="\n".join(files), + text=True, + check=False, + ) + + +@task +def clean(c): + """Clean build artifacts.""" + _log("Cleaning build artifacts...") + for name in ("build", "dist"): + _rmtree(ROOT / name) + for spec in ROOT.glob("*.spec"): + spec.unlink(missing_ok=True) + for pycache in ROOT.rglob("__pycache__"): + if ".venv" in pycache.parts: + continue + _rmtree(pycache) + for pyc in ROOT.rglob("*.pyc"): + if ".venv" in pyc.parts: + continue + pyc.unlink(missing_ok=True) + _log("Cleanup completed", GREEN) + + +@task(pre=[clean]) +def build(c): + """Build standalone executable for current platform.""" + _log("Building VoxKit...") + c.run( + "uv run --group installation python scripts/build.py build " + "--entry main.py --name VoxKit --icon ./assets/voxkit.icns --windowed" + ) + + +@task +def format(c): + """Format code with Ruff.""" + _log("Formatting code with Ruff...") + c.run("uv run --only-group dev ruff format .") + + +@task(name="format-check") +def format_check(c): + """Check code formatting with Ruff.""" + _log("Checking code formatting with Ruff...") + c.run("uv run --only-group dev ruff format --check .") + + +@task +def lint(c): + """Lint code with Ruff (auto-fix).""" + _log("Linting with Ruff...") + c.run("uv run --only-group dev ruff check --fix .") + + +@task(name="lint-check") +def lint_check(c): + """Check linting with Ruff.""" + _log("Checking linting with Ruff...") + c.run("uv run --only-group dev ruff check .") + + +@task(name="mypy-check") +def mypy_check(c): + """Run mypy for type checking.""" + _log("Running mypy for type checking...") + c.run("uv run --only-group dev mypy .") + + +@task(name="fresh-slate") +def fresh_slate(c): + """Remove virtual environment and lock file.""" + _log("Removing virtual environment and lock file...") + confirm = input("Are you sure you want to proceed? [y/N] ").strip().lower() + if confirm != "y": + print("Aborted.") + return + _rmtree(ROOT / ".venv") + (ROOT / "uv.lock").unlink(missing_ok=True) + _log("Removed .venv and uv.lock", GREEN) + + +@task(name="run-tests") +def run_tests(c): + """Run all tests (unit + GUI).""" + c.run("uv run pytest tests/") + + +@task(name="test-coverage") +def test_coverage(c): + """Run tests with detailed coverage report for core modules.""" + _log("Running tests with coverage (core modules only)...") + c.run("uv run pytest --cov=voxkit --cov-report=term-missing --cov-report=html tests/") + _log("Coverage report generated in htmlcov/index.html", GREEN) + + +@task(name="generate-coverage-badge") +def generate_coverage_badge(c): + """Generate coverage badge.""" + _log("Generating coverage badge...") + c.run("uv run pytest --cov=voxkit --cov-report=xml tests/") + c.run("uv run genbadge coverage -i coverage.xml -o assets/coverage.svg") + _log("Coverage badge updated: assets/coverage.svg", GREEN) + + +@task(name="generate-documentation") +def generate_documentation(c): + """Generate API documentation.""" + c.run("uv run --group docs pdoc -o docs src/voxkit") diff --git a/uv.lock b/uv.lock index f5e8ee3..35f12a1 100644 --- a/uv.lock +++ b/uv.lock @@ -1273,6 +1273,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, ] +[[package]] +name = "invoke" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/33/f6/227c48c5fe47fa178ccf1fda8f047d16c97ba926567b661e9ce2045c600c/invoke-3.0.3.tar.gz", hash = "sha256:437b6a622223824380bfb4e64f612711a6b648c795f565efc8625af66fb57f0c", size = 343419, upload-time = "2026-04-07T15:17:48.307Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/de/bbc12563bbf979618d17625a4e753ff7a078523e28d870d3626daa97261a/invoke-3.0.3-py3-none-any.whl", hash = "sha256:f11327165e5cbb89b2ad1d88d3292b5113332c43b8553b494da435d6ec6f5053", size = 160958, upload-time = "2026-04-07T15:17:46.875Z" }, +] + [[package]] name = "jaraco-classes" version = "3.4.0" @@ -2978,6 +2987,7 @@ dependencies = [ [package.dev-dependencies] dev = [ { name = "genbadge", extra = ["coverage"] }, + { name = "invoke" }, { name = "mypy" }, { name = "pre-commit" }, { name = "pytest" }, @@ -3017,6 +3027,7 @@ requires-dist = [ [package.metadata.requires-dev] dev = [ { name = "genbadge", extras = ["coverage"], specifier = ">=1.1.3" }, + { name = "invoke", specifier = ">=2.2.0" }, { name = "mypy", specifier = ">=1.18.2" }, { name = "pre-commit", specifier = ">=4.3.0" }, { name = "pytest", specifier = ">=8.4.2" }, From 1aaf43e7f18ae874b34f0a0bd706ca8515a8135d Mon Sep 17 00:00:00 2001 From: Beckett Frey Date: Mon, 13 Apr 2026 17:02:31 -0500 Subject: [PATCH 3/3] remove release workflow in favor of more manual steps --- .github/workflows/release.yml | 125 ---------------------------------- 1 file changed, 125 deletions(-) delete mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index af00f22..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,125 +0,0 @@ -name: Release Build - -on: - push: - tags: - - 'v*' - -permissions: - contents: write - -jobs: - build-and-release: - runs-on: macos-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - - name: Install uv - run: | - curl -LsSf https://astral.sh/uv/install.sh | sh - echo "$HOME/.cargo/bin" >> $GITHUB_PATH - - - name: Configure Git for private repos - run: | - git config --global url."https://x-access-token:${{ secrets.PRIVATE_REPO_TOKEN }}@github.com/".insteadOf "https://github.com/" - - - name: Clear uv cache - run: | - uv cache clean - - - name: Install dependencies - env: - GIT_CREDENTIALS: ${{ secrets.PRIVATE_REPO_TOKEN }} - run: | - uv sync - - - name: Build .app bundle - run: uv run invoke build - - - name: Verify build contents - run: | - echo "Checking VoxKit.app structure..." - ls -la dist/VoxKit.app/Contents/ - echo "Checking for Qt plugins..." - ls -la dist/VoxKit.app/Contents/MacOS/_internal/PyQt6/Qt6/plugins/ || echo "Qt plugins not found!" - echo "Checking for Qt platform plugin..." - ls -la dist/VoxKit.app/Contents/MacOS/_internal/PyQt6/Qt6/plugins/platforms/ || echo "Platform plugin not found!" - - - name: Get version from tag - id: get_version - run: | - VERSION=${GITHUB_REF#refs/tags/} - echo "version=$VERSION" >> $GITHUB_OUTPUT - echo "Building version: $VERSION" - - - name: Create DMG - run: | - # Create a simple DMG for easier distribution - mkdir -p dmg_contents - cp -R dist/VoxKit.app dmg_contents/ - hdiutil create -volname "VoxKit" -srcfolder dmg_contents -ov -format UDZO VoxKit.dmg - continue-on-error: true - - - name: Zip .app for release - run: | - cd dist - zip -r VoxKit.app.zip VoxKit.app - cd .. - - - name: Create Release - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ steps.get_version.outputs.version }} - release_name: VoxKit ${{ steps.get_version.outputs.version }} (macOS) - body: | - **VoxKit Release ${{ steps.get_version.outputs.version }}** - - 🍎 **Platform:** macOS (Apple Silicon & Intel) - - ## Installation - 1. Download VoxKit-macOS.app.zip - 2. Unzip the file - 3. Move VoxKit.app to your Applications folder - 4. Right-click and select "Open" the first time (macOS security) - - ## System Requirements - - macOS 10.13 or later - - Works on both Intel and Apple Silicon Macs - - ## Changes - - Built from commit: ${{ github.sha }} - - draft: false - prerelease: true - - - name: Upload .app.zip to Release - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./dist/VoxKit.app.zip - asset_name: VoxKit-macOS.app.zip - asset_content_type: application/zip - - - name: Upload DMG to Release (if created) - if: success() - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./VoxKit.dmg - asset_name: VoxKit-macOS.dmg - asset_content_type: application/x-apple-diskimage - continue-on-error: true