Skip to main content

Building & Packaging

XShell can be packaged into a single standalone executable using PyInstaller. The resulting binary requires no Python installation to run.

Requirements

python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install pyinstaller

PyInstaller is already in requirements.txt.

Build the native shell

python build.py

Output: dist/xshell (Linux/macOS) or dist/xshell.exe (Windows)

Build the web terminal

python build.py --web

Output: dist/xshell-web or dist/xshell-web.exe

The web build bundles templates/, static/, the repository themes/ folder, and the repository plugins/ folder. Start it the same way:

./dist/xshell-web # opens browser automatically

Source checkout vs packaged build

When you run XShell from source, the web app resolves templates/, static/, themes/, and plugins/ relative to the repository root.

Packaged builds copy those same top-level folders into the executable bundle, so theme and plugin discovery behaves the same way after packaging.

Clean build artifacts

python build.py --clean # removes build/ dist/ *.spec

Build the docs site

The documentation site is separate from the PyInstaller build:

cd docs-site
bun install
bun run build

Output: docs-site/build/

What build.py does

  1. Deletes build/, dist/, and any .spec files
  2. Runs PyInstaller with:
    • --onefile — single executable
  • --add-data — bundles templates/, static/, themes/, plugins/
  • --hidden-import — includes all dynamic imports (plugins, prompt_toolkit, etc.)
  1. Reports the output path and file size

Platform-specific notes

# Activate venv first
.venv\Scripts\activate
python build.py
# → dist\xshell.exe

The Windows build uses --console so output is visible in CMD/PowerShell.

Reducing build size

By default the build bundles everything. To exclude psutil if you do not use it:

  1. Open build.py
  2. Remove 'psutil' from _HIDDEN
  3. Add --exclude-module psutil to the cmd list

Troubleshooting builds

RecursionError during analysis

Add to build.py:

import sys
sys.setrecursionlimit(5000)

Missing module at runtime

Add the module to _HIDDEN in build.py:

_HIDDEN = [
...
'missing.module.name',
]

GLIBC_2.38' not found on Linux

This means the executable was built on a newer Linux system than the target machine. PyInstaller bundles Python, but it still relies on the target system's glibc being compatible with the glibc used at build time.

Check the target machine's glibc version:

ldd --version

Fix it by rebuilding on an older compatible baseline. The release workflow uses ubuntu-22.04 for this reason. Do not use the pypa manylinux Python directly for PyInstaller builds; it is built for wheel packaging and does not provide the shared Python library that PyInstaller requires.

Icon file

Place a 256×256 .ico file at static/terminal.ico and build.py will pick it up automatically.


Source map

FilePurpose
main.pyCLI entry point, argument parsing
web_app.pyFlask + Socket.IO web server
build.pyPyInstaller build script
requirements.txtPython dependencies
xshell/__init__.pyVersion string
xshell/core/shell.pyMain REPL, prompt, completions
xshell/core/parser.pyTokenizer and AST builder
xshell/core/executor.pyPipeline execution, redirects
xshell/core/builtins.py27 built-in commands
xshell/core/history.pyPersistent history
xshell/core/autocorrect.pyLevenshtein autocorrect
xshell/config/manager.pyJSON config load/save
xshell/themes/manager.pyTheme file scanner
xshell/plugins/base.pyXShellPlugin base class
xshell/plugins/manager.pyDynamic plugin loader
xshell/plugins/builtin/*.pyBundled built-in plugins (git, sysinfo, calc)
xshell/ui/renderer.pyRich output helpers
xshell/ui/prompt.pyPrompt segment builders
static/css/style.cssWeb UI styles
static/js/terminal.jsWeb UI logic
templates/index.htmlWeb terminal HTML
plugins/Project-local plugins loaded from the repo root
themes/Bundled/project-local theme JSON files
docs-site/This Docusaurus documentation site