# How to Build a Binary Distribution from Source for Code-Graph-RAG

> Learn how to build a binary distribution from source for Code-Graph-RAG. Follow simple steps to create a standalone executable from the vitali87/code-graph-rag repository.

- Repository: [Vitali Avagyan/code-graph-rag](https://github.com/vitali87/code-graph-rag)
- Tags: how-to-guide
- Published: 2026-08-13

---

**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`](https://github.com/vitali87/code-graph-rag/blob/main/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:
- [`build_binary.py`](https://github.com/vitali87/code-graph-rag/blob/main/build_binary.py) – orchestrates the entire build pipeline
- [`codebase_rag/constants/build.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/constants/build.py) – defines build constants and package lists
- [`pyproject.toml`](https://github.com/vitali87/code-graph-rag/blob/main/pyproject.toml) – declares optional dependencies including the `treesitter-full` extra

## Step-by-Step Build Process

Follow these steps to generate a distribution-ready binary:

1. **Clone the repository**

```bash
git clone https://github.com/vitali87/code-graph-rag.git
cd code-graph-rag

```

2. **Install development dependencies**

```bash

# Using uv (recommended)

uv sync --dev

# Or using pip

pip install -e .[dev]

```

3. **Install optional Tree-sitter grammars** (required for full language support)

```bash
uv sync --extra treesitter-full

# Or: pip install -e .[treesitter-full]

```

4. **Execute the build script**

```bash
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`](https://github.com/vitali87/code-graph-rag/blob/main/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`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/constants/build.py).

**Dependency Resolution**

The function `_get_treesitter_packages()` parses [`pyproject.toml`](https://github.com/vitali87/code-graph-rag/blob/main/pyproject.toml) to extract all Tree-sitter language modules declared in the `treesitter-full` optional extra:

```python
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:

```python
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`](https://github.com/vitali87/code-graph-rag/blob/main/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:

```yaml
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:

```bash
./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`](https://github.com/vitali87/code-graph-rag/blob/main/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`](https://github.com/vitali87/code-graph-rag/blob/main/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`](https://github.com/vitali87/code-graph-rag/blob/main/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`](https://github.com/vitali87/code-graph-rag/blob/main/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`](https://github.com/vitali87/code-graph-rag/blob/main/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`](https://github.com/vitali87/code-graph-rag/blob/main/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.