fix: reliable share-dir detection in cli.py and aisbf.sh; bundle aisbf.sh in package

cli.py:
- Replace hardcoded paths with sysconfig.get_path('data', scheme) covering all
  pip install modes (venv, posix_user, posix_prefix, posix_home, system)
- Fall back to legacy hardcoded paths for extra safety
- Last-resort bootstrap: copy bundled aisbf/aisbf.sh to ~/.local/share/aisbf/
  when data_files were not installed by pip (known pip/wheel limitation)
- Improved error message with all checked paths and reinstall instructions

aisbf.sh:
- Replace hardcoded /usr/share/aisbf check (wrong: setup.py installs to
  /usr/local/share/aisbf) with a Python sysconfig lookup that checks the
  actual pip data prefix across all known schemes
- Derive LOG_DIR from SHARE_DIR instead of duplicating the detection logic

Packaging:
- Add aisbf/aisbf.sh as package_data so it is always present in the installed
  aisbf package regardless of data_files extraction success
- Add recursive-include aisbf *.sh to MANIFEST.in for sdist
- Add aisbf/aisbf.sh to setup.py data_files share/aisbf/aisbf listing
parent 8c57873a
...@@ -8,6 +8,7 @@ include cli.py ...@@ -8,6 +8,7 @@ include cli.py
recursive-include config *.json recursive-include config *.json
recursive-include config *.md recursive-include config *.md
recursive-include aisbf *.py recursive-include aisbf *.py
recursive-include aisbf *.sh
recursive-include aisbf/payments/wallet *.py recursive-include aisbf/payments/wallet *.py
recursive-include templates *.html recursive-include templates *.html
recursive-include templates *.css recursive-include templates *.css
......
...@@ -23,18 +23,49 @@ ...@@ -23,18 +23,49 @@
PIDFILE="/tmp/aisbf.pid" PIDFILE="/tmp/aisbf.pid"
# Determine the correct share directory at runtime # Determine the correct share directory at runtime.
# Check for system installation first (/usr/share/aisbf) # Use Python's sysconfig so we always match the prefix pip actually used
if [ -d "/usr/share/aisbf" ]; then # (handles venvs, user installs, system installs, and custom prefixes).
SHARE_DIR="/usr/share/aisbf" _find_share_dir() {
VENV_DIR="/usr/share/aisbf/venv" python3 - <<'PYEOF'
# Running as root - use /var/log/aisbf import sysconfig, os, sys
def check(p):
return os.path.isfile(os.path.join(p, 'main.py'))
candidates = []
try:
candidates.append(os.path.join(sysconfig.get_path('data'), 'share', 'aisbf'))
except Exception:
pass
for scheme in ('posix_user', 'posix_prefix', 'posix_home'):
try:
candidates.append(os.path.join(sysconfig.get_path('data', scheme), 'share', 'aisbf'))
except Exception:
pass
candidates += ['/usr/local/share/aisbf', '/usr/share/aisbf']
seen = set()
for p in candidates:
if p not in seen:
seen.add(p)
if check(p):
print(p)
sys.exit(0)
# None found — default to user path so downstream errors are clear
import pathlib
print(pathlib.Path.home() / '.local' / 'share' / 'aisbf')
PYEOF
}
SHARE_DIR="$(_find_share_dir)"
VENV_DIR="$SHARE_DIR/venv"
# Choose log directory based on whether this is a system or user install
if [[ "$SHARE_DIR" == /usr/* ]]; then
LOG_DIR="/var/log/aisbf" LOG_DIR="/var/log/aisbf"
else else
# Fall back to user installation (~/.local/share/aisbf)
SHARE_DIR="$HOME/.local/share/aisbf"
VENV_DIR="$HOME/.local/share/aisbf/venv"
# Running as user - use ~/.local/var/log/aisbf
LOG_DIR="$HOME/.local/var/log/aisbf" LOG_DIR="$HOME/.local/var/log/aisbf"
fi fi
......
#!/bin/bash
########################################################
# Copyright (C) 2026 Stefy Lanza <stefy@nexlab.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
# Why did the programmer quit his job? Because he didn't get arrays!
########################################################
# AISBF - AI Service Broker Framework || AI Should Be Free
# This script manages the AISBF server using the installed virtual environment
PIDFILE="/tmp/aisbf.pid"
# Determine the correct share directory at runtime.
# Use Python's sysconfig so we always match the prefix pip actually used
# (handles venvs, user installs, system installs, and custom prefixes).
_find_share_dir() {
python3 - <<'PYEOF'
import sysconfig, os, sys
def check(p):
return os.path.isfile(os.path.join(p, 'main.py'))
candidates = []
try:
candidates.append(os.path.join(sysconfig.get_path('data'), 'share', 'aisbf'))
except Exception:
pass
for scheme in ('posix_user', 'posix_prefix', 'posix_home'):
try:
candidates.append(os.path.join(sysconfig.get_path('data', scheme), 'share', 'aisbf'))
except Exception:
pass
candidates += ['/usr/local/share/aisbf', '/usr/share/aisbf']
seen = set()
for p in candidates:
if p not in seen:
seen.add(p)
if check(p):
print(p)
sys.exit(0)
# None found — default to user path so downstream errors are clear
import pathlib
print(pathlib.Path.home() / '.local' / 'share' / 'aisbf')
PYEOF
}
SHARE_DIR="$(_find_share_dir)"
VENV_DIR="$SHARE_DIR/venv"
# Choose log directory based on whether this is a system or user install
if [[ "$SHARE_DIR" == /usr/* ]]; then
LOG_DIR="/var/log/aisbf"
else
LOG_DIR="$HOME/.local/var/log/aisbf"
fi
# Create log directory if it doesn't exist
mkdir -p "$LOG_DIR"
# Function to find the aisbf.json config file
# Checks user config first, then installed locations, then source tree
find_config_file() {
# Check user config first (~/.aisbf/aisbf.json)
if [ -f "$HOME/.aisbf/aisbf.json" ]; then
echo "$HOME/.aisbf/aisbf.json"
return
fi
# Check installed locations
if [ -f "/usr/share/aisbf/aisbf.json" ]; then
echo "/usr/share/aisbf/aisbf.json"
return
fi
if [ -f "$HOME/.local/share/aisbf/aisbf.json" ]; then
echo "$HOME/.local/share/aisbf/aisbf.json"
return
fi
# Check source tree config
if [ -f "$SHARE_DIR/config/aisbf.json" ]; then
echo "$SHARE_DIR/config/aisbf.json"
return
fi
# Not found
echo ""
}
# Function to get host from config file
get_host() {
local DEFAULT_HOST="127.0.0.1"
local CONFIG_FILE=$(find_config_file)
# Check if config file was found
if [ -z "$CONFIG_FILE" ]; then
echo "$DEFAULT_HOST"
return
fi
# Try to read host from config using Python
local HOST=$(python3 -c "
import json
import sys
try:
with open('$CONFIG_FILE', 'r') as f:
config = json.load(f)
server = config.get('server', {})
print(server.get('host', '$DEFAULT_HOST'))
except:
print('$DEFAULT_HOST')
" 2>/dev/null)
# Validate host is not empty
if [ -n "$HOST" ]; then
echo "$HOST"
else
echo "$DEFAULT_HOST"
fi
}
# Function to get port from config file
get_port() {
local DEFAULT_PORT=17765
local CONFIG_FILE=$(find_config_file)
# Check if config file was found
if [ -z "$CONFIG_FILE" ]; then
echo "$DEFAULT_PORT"
return
fi
# Try to read port from config using Python
local PORT=$(python3 -c "
import json
import sys
try:
with open('$CONFIG_FILE', 'r') as f:
config = json.load(f)
server = config.get('server', {})
print(server.get('port', $DEFAULT_PORT))
except:
print($DEFAULT_PORT)
" 2>/dev/null)
# Validate port is a number
if [[ "$PORT" =~ ^[0-9]+$ ]]; then
echo "$PORT"
else
echo "$DEFAULT_PORT"
fi
}
# Function to check if package was upgraded
check_package_upgrade() {
local INSTALLED_VERSION_FILE="$VENV_DIR/.aisbf_version"
local CURRENT_VERSION=$(python3 -c "import aisbf; print(aisbf.__version__)" 2>/dev/null || echo "unknown")
local SAVED_VERSION=""
if [ -f "$INSTALLED_VERSION_FILE" ]; then
SAVED_VERSION=$(cat "$INSTALLED_VERSION_FILE")
fi
if [ "$SAVED_VERSION" != "$CURRENT_VERSION" ]; then
return 0 # Needs update
fi
return 1 # No update needed
}
# Function to create venv if it doesn't exist
ensure_venv() {
if [ "$DEBUG" = "true" ]; then
echo "=== DEBUG: ensure_venv called ==="
echo "VENV_DIR: $VENV_DIR"
echo "SHARE_DIR: $SHARE_DIR"
fi
if [ ! -d "$VENV_DIR" ]; then
echo "Creating virtual environment at $VENV_DIR"
# Create venv with --system-site-packages to access system-installed aisbf
[ "$DEBUG" = "true" ] && echo "=== DEBUG: Creating venv ==="
python3 -m venv --system-site-packages "$VENV_DIR"
# Install requirements if requirements.txt exists
if [ -f "$SHARE_DIR/requirements.txt" ]; then
echo "Installing requirements from $SHARE_DIR/requirements.txt"
[ "$DEBUG" = "true" ] && echo "=== DEBUG: Installing requirements ==="
if ! "$VENV_DIR/bin/pip" install -r "$SHARE_DIR/requirements.txt"; then
echo ""
echo "=========================================="
echo "ERROR: Failed to install Python dependencies"
echo "=========================================="
echo ""
echo "Some packages require system libraries to build."
echo "If you see errors about 'coincurve', 'bip32', or 'secp256k1', install:"
echo ""
echo " Ubuntu/Debian:"
echo " sudo apt-get update"
echo " sudo apt-get install pkg-config libsecp256k1-dev build-essential"
echo ""
echo " RHEL/CentOS/Fedora:"
echo " sudo yum install pkgconfig libsecp256k1-devel gcc"
echo ""
echo " Alpine Linux:"
echo " sudo apk add pkgconfig libsecp256k1-dev gcc musl-dev"
echo ""
echo "After installing system dependencies, run this script again."
echo ""
exit 1
fi
[ "$DEBUG" = "true" ] && echo "=== DEBUG: Force reinstalling uvicorn ==="
# Force reinstall uvicorn in venv to ensure it's available inside the virtual environment
"$VENV_DIR/bin/pip" install --force-reinstall uvicorn
fi
# Save version for future upgrade detection
[ "$DEBUG" = "true" ] && echo "=== DEBUG: Saving version info ==="
python3 -c "import aisbf; print(aisbf.__version__)" > "$VENV_DIR/.aisbf_version" 2>/dev/null || echo "unknown" > "$VENV_DIR/.aisbf_version"
else
[ "$DEBUG" = "true" ] && echo "=== DEBUG: Venv already exists, checking for upgrades ==="
# Check if package was upgraded via pip
if check_package_upgrade; then
echo "Package upgrade detected, updating venv dependencies..."
# Only update requirements, aisbf is accessed from system site-packages
if [ -f "$SHARE_DIR/requirements.txt" ]; then
[ "$DEBUG" = "true" ] && echo "=== DEBUG: Updating requirements ==="
if ! "$VENV_DIR/bin/pip" install -r "$SHARE_DIR/requirements.txt"; then
echo ""
echo "=========================================="
echo "ERROR: Failed to update Python dependencies"
echo "=========================================="
echo ""
echo "Some packages require system libraries. See error messages above."
echo "You may need to install system dependencies (pkg-config, libsecp256k1-dev, build-essential)"
echo ""
exit 1
fi
[ "$DEBUG" = "true" ] && echo "=== DEBUG: Force reinstalling uvicorn ==="
# Force reinstall uvicorn in venv to ensure it's available inside the virtual environment
"$VENV_DIR/bin/pip" install --force-reinstall uvicorn
fi
python3 -c "import aisbf; print(aisbf.__version__)" > "$VENV_DIR/.aisbf_version" 2>/dev/null || echo "unknown" > "$VENV_DIR/.aisbf_version"
echo "Virtual environment updated successfully"
else
[ "$DEBUG" = "true" ] && echo "=== DEBUG: No package upgrade detected ==="
fi
fi
[ "$DEBUG" = "true" ] && echo "=== DEBUG: ensure_venv completed ==="
}
# Function to update venv packages (only install missing ones, no forced upgrades)
update_venv() {
# Only update if requirements file exists
if [ -f "$SHARE_DIR/requirements.txt" ]; then
# Check if there are any new packages to install (not already satisfied)
"$VENV_DIR/bin/pip" install -r "$SHARE_DIR/requirements.txt" 2>&1 | grep -q "Requirement already satisfied"
ALREADY_SATISFIED=$?
if [ $ALREADY_SATISFIED -ne 0 ]; then
echo "Installing new requirements (this will take a while!) ... "
if ! "$VENV_DIR/bin/pip" install -r "$SHARE_DIR/requirements.txt"; then
echo ""
echo "=========================================="
echo "ERROR: Failed to install Python dependencies"
echo "=========================================="
echo ""
echo "Some packages require system libraries. Install them with:"
echo ""
echo " Ubuntu/Debian:"
echo " sudo apt-get install pkg-config libsecp256k1-dev build-essential"
echo ""
echo " RHEL/CentOS:"
echo " sudo yum install pkgconfig libsecp256k1-devel gcc"
echo ""
exit 1
fi
# Force reinstall uvicorn in venv to ensure it's available inside the virtual environment
"$VENV_DIR/bin/pip" install --force-reinstall uvicorn
echo "[OK]"
else
echo "Virtual env already up to date"
# Still force reinstall uvicorn to ensure it's in venv
"$VENV_DIR/bin/pip" install --force-reinstall uvicorn
fi
fi
}
# Function to start the server
start_server() {
echo "=== DEBUG: Starting start_server function ==="
echo "SHARE_DIR: $SHARE_DIR"
echo "VENV_DIR: $VENV_DIR"
echo "LOG_DIR: $LOG_DIR"
# Ensure venv exists
echo "=== DEBUG: Ensuring venv exists ==="
ensure_venv
echo "=== DEBUG: Venv check complete ==="
# Get host and port from config
echo "=== DEBUG: Getting host and port ==="
HOST=$(get_host)
PORT=$(get_port)
echo "=== DEBUG: Host=$HOST, Port=$PORT ==="
# Activate the virtual environment
echo "=== DEBUG: Activating virtual environment ==="
source $VENV_DIR/bin/activate
echo "=== DEBUG: Virtual environment activated ==="
# Check Python path and imports
echo "=== DEBUG: Checking Python environment ==="
echo "Python executable: $(which python3)"
python3 -c "import sys; print('Python version:', sys.version); print('Python path:', sys.path[:3])"
echo "=== DEBUG: Testing basic imports ==="
python3 -c "import uvicorn; print('uvicorn imported successfully')" 2>&1 || echo "ERROR: Failed to import uvicorn"
python3 -c "import fastapi; print('fastapi imported successfully')" 2>&1 || echo "ERROR: Failed to import fastapi"
# Change to share directory where main.py is located
echo "=== DEBUG: Changing to share directory ==="
cd $SHARE_DIR
echo "=== DEBUG: Current directory: $(pwd) ==="
ls -la main.py 2>/dev/null || echo "WARNING: main.py not found in $SHARE_DIR"
echo "Starting AISBF on $HOST:$PORT..."
# Check if debug mode is enabled
if [ "$DEBUG" = "true" ]; then
echo "Debug mode enabled - showing all debug messages"
export AISBF_DEBUG=true
fi
# Test importing main module before starting uvicorn
echo "=== DEBUG: Testing main module import ==="
python3 -c "
try:
import main
print('main module imported successfully')
except Exception as e:
print(f'ERROR: Failed to import main module: {e}')
import traceback
traceback.print_exc()
exit(1)
" 2>&1
# Start the proxy server - runs in foreground
# Use exec to replace the shell process so signals are properly handled
echo "=== DEBUG: Starting uvicorn ==="
if [ "$DEBUG" = "true" ]; then
exec uvicorn main:app --host $HOST --port $PORT --log-level debug 2>&1 | tee -a "$LOG_DIR/aisbf_stdout.log"
else
exec uvicorn main:app --host $HOST --port $PORT 2>&1 | grep -v -E "(--- Logging error ---|BrokenPipeError|Call stack:|Message:|Arguments:)" | tee -a "$LOG_DIR/aisbf_stdout.log"
fi
}
# Function to start as daemon
start_daemon() {
# Check if already running
if [ -f "$PIDFILE" ]; then
PID=$(cat "$PIDFILE")
if ps -p "$PID" > /dev/null 2>&1; then
echo "AISBF is already running (PID: $PID)"
exit 1
else
# Stale PID file, remove it
rm -f "$PIDFILE"
fi
fi
# Ensure venv exists
ensure_venv
# Get host and port from config
HOST=$(get_host)
PORT=$(get_port)
echo "Starting AISBF on $HOST:$PORT in background..."
# Check if debug mode is enabled
if [ "$DEBUG" = "true" ]; then
echo "Debug mode enabled - showing all debug messages"
export AISBF_DEBUG=true
fi
# Start in background with nohup and logging
# Filter out BrokenPipeError logging errors
if [ "$DEBUG" = "true" ]; then
nohup bash -c "source $VENV_DIR/bin/activate && cd $SHARE_DIR && uvicorn main:app --host $HOST --port $PORT --log-level debug 2>&1" >> "$LOG_DIR/aisbf_stdout.log" 2>&1 &
else
nohup bash -c "source $VENV_DIR/bin/activate && cd $SHARE_DIR && uvicorn main:app --host $HOST --port $PORT 2>&1 | grep -v '--- Logging error ---' | grep -v 'BrokenPipeError' | grep -v 'Call stack:' | grep -v 'File .*python' | grep -v 'Message:' | grep -v 'Arguments:'" >> "$LOG_DIR/aisbf_stdout.log" 2>&1 &
fi
PID=$!
echo $PID > "$PIDFILE"
echo "AISBF started in background (PID: $PID)"
echo "Logs are being written to: $LOG_DIR"
}
# Function to check status
check_status() {
if [ -f "$PIDFILE" ]; then
PID=$(cat "$PIDFILE")
if ps -p "$PID" > /dev/null 2>&1; then
echo "AISBF is running (PID: $PID)"
exit 0
else
echo "AISBF is not running (stale PID file)"
rm -f "$PIDFILE"
exit 1
fi
else
echo "AISBF is not running"
exit 1
fi
}
# Function to stop the daemon
stop_daemon() {
if [ -f "$PIDFILE" ]; then
PID=$(cat "$PIDFILE")
if ps -p "$PID" > /dev/null 2>&1; then
kill "$PID"
rm -f "$PIDFILE"
echo "AISBF stopped (PID: $PID)"
else
echo "AISBF is not running (stale PID file)"
rm -f "$PIDFILE"
fi
else
echo "AISBF is not running"
fi
}
# Function to show help
show_help() {
echo "AISBF - AI Service Broker Framework"
echo ""
echo "Usage: aisbf.sh [OPTIONS] [COMMAND]"
echo ""
echo "Options:"
echo " --debug Enable debug mode with verbose logging"
echo " -h, --help Show this help message"
echo ""
echo "Commands:"
echo " daemon Start AISBF in background (daemon mode)"
echo " status Check if AISBF is running"
echo " stop Stop the AISBF daemon"
echo ""
echo "Examples:"
echo " aisbf.sh # Start in foreground"
echo " aisbf.sh --debug # Start with debug logging"
echo " aisbf.sh daemon # Start in background"
echo " aisbf.sh --debug daemon # Start in background with debug"
echo " aisbf.sh status # Check status"
echo " aisbf.sh stop # Stop the server"
}
# Parse command line arguments
DEBUG="false"
COMMAND=""
while [[ $# -gt 0 ]]; do
case $1 in
--debug|-d)
DEBUG="true"
shift
;;
-h|--help)
show_help
exit 0
;;
daemon|status|stop)
COMMAND="$1"
shift
;;
*)
echo "Unknown option: $1"
echo ""
show_help
exit 1
;;
esac
done
# Main command handling
case "$COMMAND" in
daemon)
start_daemon
;;
status)
check_status
;;
stop)
stop_daemon
;;
*)
# Default: start in foreground
start_server
;;
esac
...@@ -24,44 +24,113 @@ Why did the programmer quit his job? Because he didn't get arrays! ...@@ -24,44 +24,113 @@ Why did the programmer quit his job? Because he didn't get arrays!
import os import os
import sys import sys
import subprocess import subprocess
import sysconfig
import shutil
from pathlib import Path from pathlib import Path
def _share_dir_candidates():
"""Return candidate share directories in priority order, deduplicated."""
seen = set()
result = []
def add(p):
key = str(p)
if key not in seen:
seen.add(key)
result.append(Path(p))
# sysconfig paths for the running Python interpreter — covers venvs,
# system installs, and user installs regardless of scheme.
add(Path(sysconfig.get_path('data')) / 'share' / 'aisbf')
for scheme in ('posix_user', 'posix_prefix', 'posix_home'):
try:
add(Path(sysconfig.get_path('data', scheme)) / 'share' / 'aisbf')
except Exception:
pass
# Legacy hardcoded fallbacks (setup.py installs here for system-wide)
add(Path('/usr/local/share/aisbf'))
add(Path('/usr/share/aisbf'))
add(Path.home() / '.local' / 'share' / 'aisbf')
return result
def _find_share_dir():
"""Return the first candidate that contains aisbf.sh, or None."""
for candidate in _share_dir_candidates():
if (candidate / 'aisbf.sh').exists():
return candidate
return None
def _bootstrap_from_package():
"""
Last-resort: copy aisbf.sh from the bundled package data to
~/.local/share/aisbf/ so the user can at least run the script.
The other runtime files (main.py, templates, …) still need to be
present — this only fixes the 'aisbf.sh not found' error.
"""
try:
import aisbf as _pkg
bundled = Path(_pkg.__file__).parent / 'aisbf.sh'
if not bundled.exists():
return None
dest_dir = Path.home() / '.local' / 'share' / 'aisbf'
dest_dir.mkdir(parents=True, exist_ok=True)
dest = dest_dir / 'aisbf.sh'
shutil.copy2(str(bundled), str(dest))
dest.chmod(dest.stat().st_mode | 0o755)
return dest_dir
except Exception:
return None
def main(): def main():
"""Main entry point for the aisbf CLI - calls the aisbf.sh shell script""" share_dir = _find_share_dir()
# Determine the correct script path at runtime if share_dir is None:
# Check for system installation first (/usr/local/share/aisbf/aisbf.sh) share_dir = _bootstrap_from_package()
script_path = Path("/usr/local/share/aisbf/aisbf.sh") if share_dir:
if not script_path.exists(): print(
# Fall back to user installation (~/.local/share/aisbf/aisbf.sh) "Warning: AISBF data files were not installed by pip to the expected\n"
script_path = Path.home() / ".local" / "share" / "aisbf" / "aisbf.sh" f"location. Bootstrapped aisbf.sh to {share_dir}.\n"
"If the server fails to start, runtime files (main.py, templates/,\n"
# Check if the script exists "static/, config/, requirements.txt) may be missing from that directory.\n"
if not script_path.exists(): "Re-install from source to fix this:\n"
print(f"Error: AISBF script not found at {script_path}", file=sys.stderr) " pip install aisbf --no-binary aisbf",
print("Please ensure AISBF is properly installed.", file=sys.stderr) file=sys.stderr,
print(f"Expected locations:", file=sys.stderr) )
print(f" - /usr/local/share/aisbf/aisbf.sh (system-wide)", file=sys.stderr)
print(f" - ~/.local/share/aisbf/aisbf.sh (user installation)", file=sys.stderr) if share_dir is None or not (share_dir / 'aisbf.sh').exists():
checked = '\n'.join(f' - {p}' for p in _share_dir_candidates())
print(
"Error: AISBF share directory not found.\n"
"The data files may not have been installed correctly from the wheel.\n\n"
"Checked locations:\n"
f"{checked}\n\n"
"To fix, reinstall from source so pip can place files correctly:\n"
" pip install aisbf --no-binary aisbf\n"
"Or install system-wide as root:\n"
" sudo pip install aisbf",
file=sys.stderr,
)
sys.exit(1) sys.exit(1)
# Execute the shell script with all arguments passed to this Python script script_path = share_dir / 'aisbf.sh'
try: try:
# Use subprocess to run the shell script result = subprocess.run([str(script_path)] + sys.argv[1:], check=False)
result = subprocess.run(
[str(script_path)] + sys.argv[1:],
check=False
)
# Exit with the same exit code as the shell script
sys.exit(result.returncode) sys.exit(result.returncode)
except KeyboardInterrupt: except KeyboardInterrupt:
# Handle Ctrl-C gracefully sys.exit(130)
sys.exit(130) # Standard exit code for SIGINT (128 + 2)
except Exception as e: except Exception as e:
print(f"Error executing AISBF script: {e}", file=sys.stderr) print(f"Error executing AISBF script: {e}", file=sys.stderr)
sys.exit(1) sys.exit(1)
if __name__ == "__main__": if __name__ == '__main__':
main() main()
...@@ -75,7 +75,9 @@ setup( ...@@ -75,7 +75,9 @@ setup(
# install_requires=requirements, # install_requires=requirements,
include_package_data=True, include_package_data=True,
package_data={ package_data={
"aisbf": ["*.json"], # aisbf.sh bundled inside the package so cli.py can bootstrap the share
# directory even when pip fails to install data_files from the wheel.
"aisbf": ["*.json", "aisbf.sh"],
"": ["templates/**/*.html", "templates/**/*.css", "templates/**/*.js", "static/**/*"], "": ["templates/**/*.html", "templates/**/*.css", "templates/**/*.js", "static/**/*"],
}, },
data_files=[ data_files=[
...@@ -116,6 +118,7 @@ setup( ...@@ -116,6 +118,7 @@ setup(
'aisbf/analytics.py', 'aisbf/analytics.py',
'aisbf/email_utils.py', 'aisbf/email_utils.py',
'aisbf/geolocation.py', 'aisbf/geolocation.py',
'aisbf/aisbf.sh',
]), ]),
# aisbf.providers subpackage # aisbf.providers subpackage
('share/aisbf/aisbf/providers', [ ('share/aisbf/aisbf/providers', [
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment