How to Build a Binary Distribution from Source for Code-Graph-RAG
Build a standalone executable by running python build_binary.py after installing the development dependencies with uv sync --dev or pip install -e .[dev].
The Code-Graph-RAG repository provides an automated build system that packages the Python application into a single, portable binary using PyInstaller. According to the source code in vitali87/code-graph-rag, the build_binary.py script handles architecture detection, dependency bundling, and binary naming automatically.
Prerequisites
Before building, ensure your environment meets the following requirements:
- Python 3.12+ (required by the project)
- PyInstaller (included in the
devdependency group) - uv (recommended) or pip for package management
- Optional:
treesitter-fullextra for complete language support
The build process relies on three core files:
build_binary.py– orchestrates the entire build pipelinecodebase_rag/constants/build.py– defines build constants and package listspyproject.toml– declares optional dependencies including thetreesitter-fullextra
Step-by-Step Build Process
Follow these steps to generate a distribution-ready binary:
- Clone the repository
git clone https://github.com/vitali87/code-graph-rag.git
cd code-graph-rag
- Install development dependencies
# Using uv (recommended)
uv sync --dev
# Or using pip
pip install -e .[dev]
- Install optional Tree-sitter grammars (required for full language support)
uv sync --extra treesitter-full
# Or: pip install -e .[treesitter-full]
- Execute the build script
python build_binary.py
The script outputs the binary to dist/ with a platform-specific name following the template code-graph-rag-{system}-{machine} (for example, code-graph-rag-linux-amd64 or code-graph-rag-darwin-arm64).
How the Build Script Works
The build_binary.py implementation automates four critical phases:
Architecture Detection and Normalization
The script detects the host OS and CPU architecture, normalizing names like x86_64 to amd64 and aarch64/arm64 to arm64 as defined in codebase_rag/constants/build.py.
Dependency Resolution
The function _get_treesitter_packages() parses pyproject.toml to extract all Tree-sitter language modules declared in the treesitter-full optional extra:
def _get_treesitter_packages() -> list[str]:
pyproject = toml.load(cs.PYPROJECT_PATH)
extras = pyproject.get(cs.TOML_KEY_PROJECT, {}).get(
cs.TOML_KEY_OPTIONAL_DEPS, {}
)
treesitter_deps = extras.get(cs.TREESITTER_EXTRA_KEY, [])
# ...filter and normalise package names...
PyInstaller Command Construction
The script constructs a PyInstaller command with the following flags:
--onefile– creates a single-file executable--clean– removes cached builds--copy-metadata– preserves package metadata--collect-all,--collect-data,--hidden-import– bundles packages like Pydantic-AI, Rich, and Typer defined inPYINSTALLER_PACKAGES- Excludes problematic modules (e.g.,
logfire) viaPYINSTALLER_EXCLUDED_MODULES
The helper function _build_package_args() converts package configurations into CLI flags:
def _build_package_args(pkg: PyInstallerPackage) -> list[str]:
args = []
if pkg.collect_all:
args.extend([cs.PYINSTALLER_ARG_COLLECT_ALL, pkg.name])
if pkg.collect_data:
args.extend([cs.PYINSTALLER_ARG_COLLECT_DATA, pkg.name])
if pkg.hidden_import:
args.extend([cs.PYINSTALLER_ARG_HIDDEN_IMPORT, pkg.hidden_import])
return args
Post-Build Processing
Upon successful compilation, the script sets executable permissions (0o755) and reports the binary path and file size.
Customizing the Build
You can modify the build behavior through several mechanisms:
- Force specific architecture – Set the
PYINSTALLER_ARCHenvironment variable before running the script - Add packages – Extend
PYINSTALLER_PACKAGESincodebase_rag/constants/build.pywith newPyInstallerPackageentries - Exclude modules – Append import names to
PYINSTALLER_EXCLUDED_MODULESin the constants file
CI/CD Integration
Automate binary generation in GitHub Actions using the following workflow:
name: Build Binary
on:
push:
tags: ["v*"]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install uv & dependencies
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
uv sync --dev --extra treesitter-full
- name: Build binary
run: python build_binary.py
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: code-graph-rag-binary
path: dist/*
This configuration produces reproducible binaries for Linux, macOS, or Windows runners without requiring Python on target machines.
Verification
Test the generated binary immediately after building:
./dist/code-graph-rag-linux-amd64 --help
You should see the Typer-generated CLI help output, confirming that all dependencies and entry points are correctly bundled.
Summary
- The
build_binary.pyscript invitali87/code-graph-ragautomates PyInstaller execution with architecture detection and dependency bundling. - Tree-sitter grammars are resolved dynamically from
pyproject.tomlwhen thetreesitter-fullextra is installed. - The binary naming convention follows
code-graph-rag-{system}-{machine}(e.g.,code-graph-rag-linux-amd64). - Build constants and package lists are centralized in
codebase_rag/constants/build.py. - The resulting single-file executable requires no external Python interpreter on the target machine.
Frequently Asked Questions
What is the minimum Python version required to build the binary?
The build system requires Python 3.12 or higher, as specified in the pyproject.toml project configuration. This ensures compatibility with the type hints and syntax used throughout the codebase.
Can I build the binary without installing all Tree-sitter languages?
Yes. The treesitter-full extra is optional. If you skip uv sync --extra treesitter-full, the resulting binary will only support the core Tree-sitter languages bundled by default. The build script calls _get_treesitter_packages() to detect available grammars at build time, so the binary size adapts to your installation.
How do I exclude specific packages from the binary?
Modify the PYINSTALLER_EXCLUDED_MODULES list in codebase_rag/constants/build.py. The default configuration already excludes problematic modules like logfire. Append any additional import names you wish to exclude to prevent them from being bundled into the final executable.
Why does the binary name include system and architecture identifiers?
The naming convention code-graph-rag-{system}-{machine} (defined in BINARY_NAME_TEMPLATE in constants/build.py) ensures platform compatibility. PyInstaller creates binaries tied to the build host's operating system and CPU architecture, so the identifier prevents deployment mismatches between, for example, an amd64 Linux build and an arm64 macOS target.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →