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
- Deletes
build/,dist/, and any.specfiles - Runs PyInstaller with:
--onefile— single executable
--add-data— bundlestemplates/,static/,themes/,plugins/--hidden-import— includes all dynamic imports (plugins, prompt_toolkit, etc.)
- Reports the output path and file size
Platform-specific notes
- Windows
- macOS
- Linux
# Activate venv first
.venv\Scripts\activate
python build.py
# → dist\xshell.exe
The Windows build uses --console so output is visible in CMD/PowerShell.
source .venv/bin/activate
python build.py
# → dist/xshell
# Make executable (should already be set)
chmod +x dist/xshell
./dist/xshell
source .venv/bin/activate
python build.py
# → dist/xshell
./dist/xshell
Linux PyInstaller binaries inherit the glibc baseline from the machine that builds them. If you build on a newer distro, such as Ubuntu 24.04, the binary may fail on older distros with an error like:
version `GLIBC_2.38' not found
For release binaries or anything you plan to share across Linux distros, build
on the oldest supported distro you can reasonably target. The GitHub Actions
workflow uses ubuntu-22.04; avoid ubuntu-latest because it can move to a
newer glibc baseline.
python -m pip install -r requirements.txt
python build.py
To install system-wide:
sudo cp dist/xshell /usr/local/bin/xshell
xshell
Reducing build size
By default the build bundles everything. To exclude psutil if you do not use it:
- Open
build.py - Remove
'psutil'from_HIDDEN - Add
--exclude-module psutilto thecmdlist
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
| File | Purpose |
|---|---|
main.py | CLI entry point, argument parsing |
web_app.py | Flask + Socket.IO web server |
build.py | PyInstaller build script |
requirements.txt | Python dependencies |
xshell/__init__.py | Version string |
xshell/core/shell.py | Main REPL, prompt, completions |
xshell/core/parser.py | Tokenizer and AST builder |
xshell/core/executor.py | Pipeline execution, redirects |
xshell/core/builtins.py | 27 built-in commands |
xshell/core/history.py | Persistent history |
xshell/core/autocorrect.py | Levenshtein autocorrect |
xshell/config/manager.py | JSON config load/save |
xshell/themes/manager.py | Theme file scanner |
xshell/plugins/base.py | XShellPlugin base class |
xshell/plugins/manager.py | Dynamic plugin loader |
xshell/plugins/builtin/*.py | Bundled built-in plugins (git, sysinfo, calc) |
xshell/ui/renderer.py | Rich output helpers |
xshell/ui/prompt.py | Prompt segment builders |
static/css/style.css | Web UI styles |
static/js/terminal.js | Web UI logic |
templates/index.html | Web terminal HTML |
plugins/ | Project-local plugins loaded from the repo root |
themes/ | Bundled/project-local theme JSON files |
docs-site/ | This Docusaurus documentation site |