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 dev dependency group)
  • uv (recommended) or pip for package management
  • Optional: treesitter-full extra for complete language support

The build process relies on three core files:

Step-by-Step Build Process

Follow these steps to generate a distribution-ready binary:

  1. Clone the repository
git clone https://github.com/vitali87/code-graph-rag.git
cd code-graph-rag
  1. Install development dependencies

# Using uv (recommended)

uv sync --dev

# Or using pip

pip install -e .[dev]
  1. Install optional Tree-sitter grammars (required for full language support)
uv sync --extra treesitter-full

# Or: pip install -e .[treesitter-full]
  1. 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 in PYINSTALLER_PACKAGES
  • Excludes problematic modules (e.g., logfire) via PYINSTALLER_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_ARCH environment variable before running the script
  • Add packages – Extend PYINSTALLER_PACKAGES in codebase_rag/constants/build.py with new PyInstallerPackage entries
  • Exclude modules – Append import names to PYINSTALLER_EXCLUDED_MODULES in 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.py script in vitali87/code-graph-rag automates PyInstaller execution with architecture detection and dependency bundling.
  • Tree-sitter grammars are resolved dynamically from pyproject.toml when the treesitter-full extra 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →