Skip to content

build(deps): bump phonenumbers from 9.0.36 to 9.0.37 #291

build(deps): bump phonenumbers from 9.0.36 to 9.0.37

build(deps): bump phonenumbers from 9.0.36 to 9.0.37 #291

name: "Build Executable"
on:
push:
branches: [main]
pull_request: {}
release:
types: [created]
jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- os: windows-latest
artifact: kuckuck_windows
binary: kuckuck_windows.exe
- os: macos-latest
artifact: kuckuck_macos_arm64
binary: kuckuck_macos_arm64
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v7
- name: Set up uv
uses: astral-sh/setup-uv@v10.0.1
with:
python-version: "3.14"
# Single-binary release (~ 300 MB). Bundles the CLI entry point
# (src/kuckuck/__main__.py), the FastMCP server (invoked as
# ``kuckuck mcp serve``), and the full NER stack (gliner + CPU-only
# torch + transformers) so the downloaded executable supports the CLI,
# the install-claude-hook flow, and the MCP-stdio server in one go.
#
# CPU-only torch is critical: a default install pulls ~3 GB of
# CUDA / triton / nvidia libraries that we never use at runtime. The
# +cpu wheels from pytorch.org are ~250 MB and produce a ~300 MB binary.
# Installed with `uv pip` (not `uv sync`) so this preinstalled CPU
# wheel is what satisfies gliner's torch requirement, instead of
# whatever uv.lock resolved.
#
# Model weights themselves are NOT bundled - they live in
# ~/.cache/kuckuck/models after 'kuckuck fetch-model'. Bundling them
# would push the binary past 1 GB without giving users a way to pick a
# different model.
- name: Install build dependencies
shell: bash
run: |
uv venv
uv pip install -e ".[cli,mcp]" --group build_executable
uv pip install --index-url https://download.pytorch.org/whl/cpu torch
uv pip install 'gliner>=0.2.20' 'huggingface-hub>=0.20' 'onnxruntime>=1.18'
- name: Build Executable
shell: bash
run: |
uv run --no-sync pyinstaller --onefile --name ${{ matrix.artifact }} \
--collect-data kuckuck \
--collect-submodules kuckuck_mcp \
--collect-all fastmcp \
--collect-all mcp \
--collect-all gliner \
--collect-all huggingface_hub \
--collect-submodules transformers \
--collect-data transformers \
--collect-all tokenizers \
--collect-data torch \
--collect-binaries torch \
--collect-all sentencepiece \
--collect-all safetensors \
--copy-metadata torch \
--copy-metadata gliner \
--copy-metadata transformers \
--copy-metadata huggingface_hub \
--copy-metadata tokenizers \
--exclude-module nvidia \
--exclude-module triton \
--exclude-module cuda \
--exclude-module tensorflow \
--exclude-module jax \
src/kuckuck/__main__.py
# macOS Apple Silicon refuses to execute unsigned Mach-O binaries (amfi
# kills them with SIGKILL regardless of Gatekeeper settings). Ad-hoc
# signing ("codesign -s -") is free and sufficient for local use and
# for binaries distributed via GitHub releases after the user clears
# the quarantine xattr:
# xattr -c kuckuck_macos_arm64
- name: Codesign (macOS)
if: matrix.os == 'macos-latest'
run: codesign --force --deep --sign - dist/${{ matrix.binary }}
- name: Freeze build dependencies
run: uv pip freeze > dist/build-dependencies-${{ matrix.artifact }}.txt
shell: bash
- name: Upload Executable as Artifact
uses: actions/upload-artifact@v7
with:
name: ${{ matrix.artifact }}
path: |
dist/${{ matrix.binary }}
dist/build-dependencies-${{ matrix.artifact }}.txt
# Cache the GLiNER model snapshot once across the matrix so the NER smoke
# test does not re-download 1.1 GB on every run. Subsequent matrix entries
# restore from this cache via the actions/cache restore step.
fetch_ner_model:
runs-on: ubuntu-latest
name: "Fetch GLiNER model (cached)"
steps:
- name: Restore model cache
id: cache
uses: actions/cache@v6
with:
path: ~/.cache/kuckuck/models
key: gliner-multi-v2.1
- name: Set up uv
if: steps.cache.outputs.cache-hit != 'true'
uses: astral-sh/setup-uv@v10.0.1
with:
python-version: "3.14"
- name: Download model
if: steps.cache.outputs.cache-hit != 'true'
run: |
uv venv
uv pip install huggingface-hub
uv run --no-sync python -c "from huggingface_hub import snapshot_download; \
snapshot_download(repo_id='urchade/gliner_multi-v2.1', \
local_dir='${HOME}/.cache/kuckuck/models/gliner_multi-v2.1')"
smoke_test:
# fetch_ner_model is in the needs list so the NER smoke test always runs
# against a populated model cache. Before the single-binary collapse a
# missing cache silently skipped the NER leg - fine back then because a
# separate slim variant was the ultimate safety net, but with one fat
# binary as the only artifact there is no slimmer fallback to catch a
# NER regression.
needs: [build, fetch_ner_model]
strategy:
fail-fast: false
matrix:
include:
- os: windows-latest
artifact: kuckuck_windows
binary: kuckuck_windows.exe
- os: macos-latest
artifact: kuckuck_macos_arm64
binary: kuckuck_macos_arm64
runs-on: ${{ matrix.os }}
name: "Smoke Test (${{ matrix.artifact }})"
steps:
- uses: actions/checkout@v7
- name: Set up Python
uses: actions/setup-python@v7
with:
python-version: "3.14"
- name: Download Executable
uses: actions/download-artifact@v8
with:
name: ${{ matrix.artifact }}
path: dist
# actions/upload-artifact zips contents and the zip format does not
# reliably preserve Unix mode bits on download — restore +x.
- name: Make binary executable (macOS)
if: matrix.os == 'macos-latest'
run: chmod +x dist/${{ matrix.binary }}
shell: bash
- name: Restore GLiNER model cache
uses: actions/cache@v6
with:
path: ~/.cache/kuckuck/models
key: gliner-multi-v2.1
- name: Smoke Test
run: python unittests/smoke_test_exe.py dist/${{ matrix.binary }}
- name: NER Smoke Test
run: python unittests/smoke_test_exe_ner.py dist/${{ matrix.binary }}
shell: bash
release:
needs: [build, smoke_test]
if: github.event_name == 'release'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Download Windows Executable
uses: actions/download-artifact@v8
with:
name: kuckuck_windows
path: dist/windows
continue-on-error: true
- name: Download macOS Executable
uses: actions/download-artifact@v8
with:
name: kuckuck_macos_arm64
path: dist/macos
continue-on-error: true
- name: Rename Executables with version
run: |
set -eu
mkdir -p dist/release
if [ -f ./dist/windows/kuckuck_windows.exe ]; then
mv ./dist/windows/kuckuck_windows.exe \
./dist/release/kuckuck_windows_${{ github.ref_name }}.exe
else
echo "::warning::Windows artifact missing; skipping"
fi
if [ -f ./dist/macos/kuckuck_macos_arm64 ]; then
mv ./dist/macos/kuckuck_macos_arm64 \
./dist/release/kuckuck_macos_arm64_${{ github.ref_name }}
else
echo "::warning::macOS arm64 artifact missing; skipping"
fi
ls -la dist/release/
- name: Upload Executable to Release
uses: softprops/action-gh-release@v3
with:
files: |
./dist/release/kuckuck_windows_${{ github.ref_name }}.exe
./dist/release/kuckuck_macos_arm64_${{ github.ref_name }}
fail_on_unmatched_files: false