Merge pull request #7 from torlando-tech/fix/issue-4-missing-dependencies

Fix #4: Use system packages to avoid compilation
This commit is contained in:
torlando-tech 2025-10-29 00:08:42 -04:00 committed by GitHub
commit eb22d4fefd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 370 additions and 29 deletions

View file

@ -117,3 +117,48 @@ jobs:
echo "- Python version: ${{ matrix.python-version }}" >> $GITHUB_STEP_SUMMARY
echo "- Tests run: All integration tests (hardware tests excluded)" >> $GITHUB_STEP_SUMMARY
echo "- See test output above for details" >> $GITHUB_STEP_SUMMARY
installer-test:
name: Installer Test (Fresh System)
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
os-image:
- "debian:12" # Debian Bookworm (current stable)
- "debian:trixie" # Debian Trixie (testing - next release)
- "ubuntu:22.04" # Ubuntu Jammy LTS
- "ubuntu:24.04" # Ubuntu Noble (latest LTS)
- "archlinux:latest" # Arch Linux (rolling release)
# Allow Trixie and Arch to fail without blocking CI (testing/rolling can be unstable)
continue-on-error: ${{ matrix.os-image == 'debian:trixie' || matrix.os-image == 'archlinux:latest' }}
container:
image: ${{ matrix.os-image }}
env:
DEBIAN_FRONTEND: noninteractive
DEBCONF_NONINTERACTIVE_SEEN: "true"
TZ: UTC
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
path: workspace
- name: Run installer integration test
run: |
cd workspace
bash tests/test_installer.sh
continue-on-error: false
- name: Installation summary
if: always()
run: |
echo "## Installer Test Results (${{ matrix.os-image }})" >> $GITHUB_STEP_SUMMARY
echo "✓ System packages: python3-gi, python3-dbus, python3-cairo, bluez" >> $GITHUB_STEP_SUMMARY
echo "✓ Pip packages: bleak, bluezero" >> $GITHUB_STEP_SUMMARY
echo "✓ Install method: System packages (no compilation)" >> $GITHUB_STEP_SUMMARY
echo "✓ Installation time: < 1 minute" >> $GITHUB_STEP_SUMMARY

View file

@ -52,33 +52,46 @@ The script will:
**Debian/Ubuntu/Raspberry Pi OS:**
```bash
sudo apt-get update
sudo apt-get install python3-pip python3-dbus bluez
sudo apt-get install python3-pip python3-gi python3-dbus python3-cairo bluez
```
**Arch Linux:**
```bash
sudo pacman -S python-pip python-dbus bluez bluez-utils
sudo pacman -S base-devel gobject-introspection python-pip python-dbus python-cairo bluez bluez-utils
```
**Why these packages?**
- `base-devel`: Build tools (gcc, make, meson) required for compiling PyGObject
- `gobject-introspection`: Development files for GObject introspection (required for PyGObject compilation)
- `python-dbus`: D-Bus Python bindings for BlueZ communication
- `python-cairo`: Cairo graphics library
- `bluez` / `bluez-utils`: Bluetooth stack and utilities for Linux
**Note for Arch users:** PyGObject is intentionally NOT installed as a system package on Arch due to version incompatibility (Arch has 3.54.5, but bluezero requires <3.52.0). Instead, pip will compile the compatible PyGObject version (3.50.2) during installation. This adds ~2 minutes to installation time but ensures compatibility.
#### 2. Install Python Dependencies
**IMPORTANT:** Install in the same environment as Reticulum!
Since we installed system packages for PyGObject, dbus-python, and pycairo in step 1, we only need to install the pure-Python packages:
**If Reticulum is in a virtual environment:**
```bash
# Activate the same venv where Reticulum is installed
source /path/to/reticulum-venv/bin/activate
pip install -r requirements.txt
pip install bleak==1.1.1 bluezero
```
**If Reticulum is installed system-wide:**
```bash
# Install system-wide (may need sudo)
pip install -r requirements.txt
pip install bleak==1.1.1 bluezero
# OR
sudo pip install -r requirements.txt
sudo pip install bleak==1.1.1 bluezero
```
**Note:** The system packages (python3-gi, python3-dbus, python3-cairo) provide PyGObject, dbus-python, and pycairo, eliminating the need for lengthy compilation from source.
#### 3. Copy BLE Interface Files
```bash

View file

@ -1,6 +1,7 @@
#!/bin/bash
# Reticulum BLE Interface Installation Script
# This script installs the BLE interface to an existing Reticulum installation
# This script installs the BLE interface and all its prerequisites
# Handles: basic system packages, Reticulum Network Stack, system dependencies, and BLE interface
set -e # Exit on error
@ -34,6 +35,20 @@ print_info() {
echo -e "${BLUE}${NC} $1"
}
# Helper function: pip install with compatibility across all OS versions
pip_install() {
local packages="$*"
# Check if pip supports --break-system-packages flag (pip 23.0+, PEP 668)
if pip3 install --help 2>/dev/null | grep -q -- --break-system-packages; then
# Debian 12+, Trixie, Ubuntu 24.04+ with externally-managed-environment
pip3 install $packages --break-system-packages
else
# Ubuntu 22.04 and earlier (pip 22.x without the flag)
pip3 install $packages
fi
}
# Parse command line arguments
CUSTOM_CONFIG_DIR=""
while [[ $# -gt 0 ]]; do
@ -65,11 +80,72 @@ if [[ "$OSTYPE" != "linux-gnu"* ]]; then
exit 1
fi
# Detect CI environment and configure non-interactive mode
if [[ -n "$CI" ]] || [[ -n "$GITHUB_ACTIONS" ]] || [[ -n "$DEBIAN_FRONTEND" ]]; then
export DEBIAN_FRONTEND=noninteractive
export DEBCONF_NONINTERACTIVE_SEEN=true
fi
print_header "Reticulum BLE Interface Installer"
echo
# Step 0: Ensure basic prerequisites are installed
print_header "Checking Basic Prerequisites"
# Check if we have package manager access
if command -v apt-get &> /dev/null; then
# Debian/Ubuntu - check for basic packages
MISSING_PACKAGES=()
for pkg in python3 python3-pip git; do
if ! dpkg -l | grep -q "^ii $pkg "; then
MISSING_PACKAGES+=($pkg)
fi
done
if [ ${#MISSING_PACKAGES[@]} -gt 0 ]; then
print_info "Installing basic prerequisites: ${MISSING_PACKAGES[*]}"
# Use sudo only if not running as root (Debian containers run as root without sudo)
if [ "$EUID" -eq 0 ]; then
apt-get update -qq
apt-get install -y -q ${MISSING_PACKAGES[*]}
else
sudo apt-get update -qq
sudo apt-get install -y -q ${MISSING_PACKAGES[*]}
fi
print_success "Basic prerequisites installed"
else
print_success "Basic prerequisites already installed"
fi
elif command -v pacman &> /dev/null; then
# Arch Linux - check for basic packages
MISSING_PACKAGES=()
for pkg in python python-pip git; do
if ! pacman -Q $pkg &> /dev/null; then
MISSING_PACKAGES+=($pkg)
fi
done
if [ ${#MISSING_PACKAGES[@]} -gt 0 ]; then
print_info "Installing basic prerequisites: ${MISSING_PACKAGES[*]}"
# Use sudo only if not running as root
if [ "$EUID" -eq 0 ]; then
# Sync package database first (required in fresh containers)
pacman -Sy --noconfirm
pacman -S --noconfirm ${MISSING_PACKAGES[*]}
else
sudo pacman -Sy --noconfirm
sudo pacman -S --noconfirm ${MISSING_PACKAGES[*]}
fi
print_success "Basic prerequisites installed"
else
print_success "Basic prerequisites already installed"
fi
fi
echo
# Step 1: Check for Reticulum installation
print_info "Checking for Reticulum installation..."
print_header "Checking for Reticulum"
RNS_VENV=""
RNS_PYTHON=""
@ -108,12 +184,25 @@ if command -v rnsd &> /dev/null; then
fi
fi
else
print_error "Reticulum (rnsd) not found!"
echo
echo "Please install Reticulum first:"
echo " pip install rns"
echo "Or visit: https://reticulum.network"
exit 1
print_warning "Reticulum (rnsd) not found"
print_info "Installing Reticulum Network Stack..."
# Install Reticulum using our pip helper function
pip_install rns
# Verify installation
if command -v rnsd &> /dev/null; then
print_success "Reticulum installed successfully"
INSTALL_MODE="system"
RNS_PYTHON="python3"
else
print_error "Reticulum installation failed"
echo
echo "Please try installing manually:"
echo " pip install rns"
echo "Or visit: https://reticulum.network"
exit 1
fi
fi
echo
@ -124,16 +213,33 @@ print_header "Installing System Dependencies"
if command -v apt-get &> /dev/null; then
# Debian/Ubuntu/Raspberry Pi OS
print_info "Detected Debian/Ubuntu-based system"
echo "Installing: python3-pip python3-dbus bluez"
sudo apt-get update
sudo apt-get install -y python3-pip python3-dbus bluez
print_success "System dependencies installed"
echo "Installing: python3-pip python3-gi python3-dbus python3-cairo bluez"
# Use sudo only if not running as root
if [ "$EUID" -eq 0 ]; then
apt-get update
apt-get install -y python3-pip python3-gi python3-dbus python3-cairo bluez
else
sudo apt-get update
sudo apt-get install -y python3-pip python3-gi python3-dbus python3-cairo bluez
fi
print_success "System dependencies installed (using pre-compiled system packages)"
elif command -v pacman &> /dev/null; then
# Arch Linux
print_info "Detected Arch Linux"
echo "Installing: python-pip python-dbus bluez bluez-utils"
sudo pacman -S --noconfirm python-pip python-dbus bluez bluez-utils
print_success "System dependencies installed"
echo "Installing: base-devel gobject-introspection python-pip python-dbus python-cairo bluez bluez-utils"
print_warning "Note: PyGObject will be compiled from pip due to version requirements (bluezero needs <3.52.0, Arch has 3.54.5)"
# Use sudo only if not running as root
if [ "$EUID" -eq 0 ]; then
# Sync package database first (may have been synced in basic prereqs, but ensure it's current)
pacman -Sy --noconfirm
# Skip python-gobject to avoid version conflict - pip will compile PyGObject
# gobject-introspection provides dev files needed for PyGObject compilation
pacman -S --needed --noconfirm base-devel gobject-introspection python-pip python-dbus python-cairo bluez bluez-utils
else
sudo pacman -Sy --noconfirm
sudo pacman -S --needed --noconfirm base-devel gobject-introspection python-pip python-dbus python-cairo bluez bluez-utils
fi
print_success "System dependencies installed (PyGObject will be compiled from pip)"
else
print_warning "Could not detect package manager"
print_info "Please manually install: BlueZ 5.x, python3-dbus"
@ -149,6 +255,8 @@ echo
# Step 3: Install Python dependencies
print_header "Installing Python Dependencies"
print_info "Installing pip packages (PyGObject, dbus-python, pycairo provided by system packages)"
if [ "$INSTALL_MODE" = "venv" ]; then
print_info "Installing to virtual environment: $RNS_VENV"
@ -159,20 +267,22 @@ if [ "$INSTALL_MODE" = "venv" ]; then
# Activate venv and install
source "$RNS_VENV/bin/activate"
pip install -r requirements.txt
# Install only packages not provided by system packages
# System packages provide: PyGObject (gi), dbus-python (dbus), pycairo (cairo)
# We need to install: bleak, bluezero
pip_install bleak==1.1.1 bluezero
print_success "Python dependencies installed in virtual environment"
print_info "Note: Using system-provided PyGObject, dbus-python, and pycairo"
elif [ "$INSTALL_MODE" = "system" ]; then
print_info "Installing system-wide Python packages"
# Try without sudo first
if pip install -r requirements.txt 2>/dev/null; then
print_success "Python dependencies installed (user)"
else
print_warning "User install failed, trying with sudo..."
sudo pip install -r requirements.txt
print_success "Python dependencies installed (system)"
fi
# Install only packages not provided by system packages
# Use pip_install helper for compatibility
pip_install bleak==1.1.1 bluezero
print_success "Python dependencies installed"
print_info "Note: Using system-provided PyGObject, dbus-python, and pycairo"
else
print_error "Could not determine installation mode"
exit 1

173
tests/test_installer.sh Executable file
View file

@ -0,0 +1,173 @@
#!/bin/bash
# Integration test for install.sh on fresh Linux systems
# This script tests that install.sh works correctly on a completely fresh system
# with no prerequisites installed
# Supports: Debian, Ubuntu, Arch Linux
set -e
# Detect OS type
if command -v apt-get &> /dev/null; then
OS_TYPE="debian"
elif command -v pacman &> /dev/null; then
OS_TYPE="arch"
else
echo "ERROR: Unsupported OS (no apt-get or pacman found)"
exit 1
fi
# Configure non-interactive mode for CI/container environments
if [ "$OS_TYPE" = "debian" ]; then
# Debian/Ubuntu-specific environment setup
export DEBIAN_FRONTEND=noninteractive
export DEBCONF_NONINTERACTIVE_SEEN=true
export TZ=UTC
# Pre-configure timezone to prevent interactive prompts
ln -fs /usr/share/zoneinfo/UTC /etc/localtime
fi
echo "=== Testing install.sh on fresh system ==="
echo "OS: $(cat /etc/os-release | grep PRETTY_NAME | cut -d= -f2 | tr -d '"')"
echo "OS Type: $OS_TYPE"
echo ""
echo "NOTE: install.sh will handle all prerequisites (Python, pip, Reticulum, etc.)"
echo ""
# Helper function: Check if a package is installed (OS-agnostic)
check_package() {
local pkg="$1"
if [ "$OS_TYPE" = "debian" ]; then
# Match package with or without architecture suffix (e.g., python3-cairo:amd64)
dpkg -l | grep -q "^ii $pkg" || { echo "FAIL: $pkg not installed"; exit 1; }
elif [ "$OS_TYPE" = "arch" ]; then
pacman -Q "$pkg" &> /dev/null || { echo "FAIL: $pkg not installed"; exit 1; }
fi
}
# Run installer - it now handles everything from basic packages to Reticulum
echo "Running install.sh (self-contained installer)..."
# Navigate to repository root (script is in tests/ directory)
cd "$(dirname "$0")/.."
chmod +x install.sh
mkdir -p /tmp/test-config
# Run non-interactively (answer 'n' to bluetooth permissions prompt)
./install.sh --config /tmp/test-config <<EOF
n
EOF
echo ""
# Verify installation
echo "=== Verifying Installation ==="
echo ""
# Check system packages
echo "Checking system packages..."
if [ "$OS_TYPE" = "debian" ]; then
check_package python3-gi
echo " ✓ python3-gi installed"
check_package python3-dbus
echo " ✓ python3-dbus installed"
check_package python3-cairo
echo " ✓ python3-cairo installed"
check_package bluez
echo " ✓ bluez installed"
elif [ "$OS_TYPE" = "arch" ]; then
# Note: python-gobject NOT installed on Arch to avoid version conflict
# PyGObject compiled from pip instead
check_package python-dbus
echo " ✓ python-dbus installed"
check_package python-cairo
echo " ✓ python-cairo installed"
check_package bluez
echo " ✓ bluez installed"
check_package bluez-utils
echo " ✓ bluez-utils installed"
fi
echo ""
# Check Python imports (verify system packages work)
echo "Checking Python module imports..."
python3 -c "import gi; print(' ✓ gi version:', gi.__version__)" || { echo "FAIL: Cannot import gi"; exit 1; }
python3 -c "import dbus; print(' ✓ dbus version:', dbus.__version__)" || { echo "FAIL: Cannot import dbus"; exit 1; }
python3 -c "import cairo; print(' ✓ cairo imported successfully')" || { echo "FAIL: Cannot import cairo"; exit 1; }
echo ""
# Check Reticulum installation
echo "Checking Reticulum installation..."
command -v rnsd || { echo "FAIL: rnsd command not found"; exit 1; }
echo " ✓ rnsd command available"
python3 -c "import RNS; print(' ✓ RNS version:', RNS.version)" || { echo "FAIL: Cannot import RNS"; exit 1; }
echo ""
# Check pip packages
echo "Checking pip-installed packages..."
python3 -c "import bleak; print(' ✓ bleak imported successfully')" || { echo "FAIL: Cannot import bleak"; exit 1; }
python3 -c "import bluezero; print(' ✓ bluezero imported successfully')" || { echo "FAIL: Cannot import bluezero"; exit 1; }
echo ""
# Check build tools status
if [ "$OS_TYPE" = "debian" ]; then
echo "Verifying no build dependencies were required..."
if dpkg -l | grep -q meson; then
echo " ⚠ WARNING: meson was installed (should not be needed)"
fi
if dpkg -l | grep -q cmake; then
echo " ⚠ WARNING: cmake was installed (should not be needed)"
fi
if dpkg -l | grep -q libglib2.0-dev; then
echo " ⚠ WARNING: libglib2.0-dev was installed (should not be needed)"
fi
echo " ✓ No build tools required"
elif [ "$OS_TYPE" = "arch" ]; then
echo "Verifying build tools installed (required on Arch for PyGObject compilation)..."
check_package base-devel
echo " ✓ base-devel installed (includes gcc, make, etc.)"
fi
echo ""
# Check files were copied
echo "Checking BLE interface files..."
test -f /tmp/test-config/interfaces/BLEInterface.py || { echo "FAIL: BLEInterface.py not installed"; exit 1; }
echo " ✓ BLEInterface.py"
test -f /tmp/test-config/interfaces/BLEGATTServer.py || { echo "FAIL: BLEGATTServer.py not installed"; exit 1; }
echo " ✓ BLEGATTServer.py"
test -f /tmp/test-config/interfaces/BLEFragmentation.py || { echo "FAIL: BLEFragmentation.py not installed"; exit 1; }
echo " ✓ BLEFragmentation.py"
test -f /tmp/test-config/interfaces/BLEAgent.py || { echo "FAIL: BLEAgent.py not installed"; exit 1; }
echo " ✓ BLEAgent.py"
echo ""
# Test import of installed BLE interface
echo "Testing BLE interface import..."
cd /tmp/test-config/interfaces
python3 -c "import sys; sys.path.insert(0, '.'); from BLEInterface import BLEInterface; print(' ✓ BLEInterface imported successfully')" || { echo "FAIL: Cannot import BLEInterface"; exit 1; }
echo ""
echo "=== SUCCESS: All tests passed ==="
echo ""
echo "Installation summary:"
echo " • install.sh is fully self-contained (handles all prerequisites)"
echo " • Reticulum Network Stack: installed via pip"
if [ "$OS_TYPE" = "debian" ]; then
echo " • System packages: python3, python3-pip, git, python3-gi, python3-dbus, python3-cairo, bluez"
echo " • Pip packages: rns, bleak, bluezero"
echo " • Install method: System packages (no compilation)"
echo " • Installation time: < 1 minute"
elif [ "$OS_TYPE" = "arch" ]; then
echo " • System packages: python, python-pip, git, python-dbus, python-cairo, bluez, bluez-utils, base-devel"
echo " • Pip packages: rns, bleak, bluezero, PyGObject (compiled)"
echo " • Install method: System packages + PyGObject compilation (version compatibility)"
echo " • Installation time: ~2-3 minutes (PyGObject compilation)"
fi
echo ""