Add build scripts and setup for pyMC_Repeater

- Implemented build-dev.sh for creating development .deb packages from untagged commits.
- Implemented build-prod.sh for creating production .deb packages from tagged commits, including checks for a clean git state.
- Added setup-build-env.sh to automate the installation of required build dependencies for Debian/Ubuntu.
- Created setup.py to manage package setup using setuptools with versioning from git tags.
This commit is contained in:
Lloyd
2025-12-30 15:17:48 +00:00
parent 7112da98c2
commit 59a151f382
24 changed files with 1057 additions and 413 deletions

1
.gitignore vendored
View File

@@ -7,6 +7,7 @@ __pycache__/
*.so
.Python
build/
repeater/_version.py
develop-eggs/
dist/
downloads/

View File

@@ -0,0 +1,10 @@
[clean]
all=1
[build]
build_lib=/home/rightup/Documents/GIT/meshcore/pyMC_Repeater/.pybuild/cpython3_3.12_pymc-repeater/build
[install]
force=1
install_layout=deb
install_scripts=$base/bin
install_lib=/usr/lib/python3.12/dist-packages
prefix=/usr

6
debian/.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
*.debhelper
*.debhelper.log
*.substvars
.debhelper/
files
pymc-repeater/

6
debian/changelog vendored Normal file
View File

@@ -0,0 +1,6 @@
pymc-repeater (1.0.5~dev0) unstable; urgency=medium
* Development build from git commit 7112da9
* Version: 1.0.5.post0
-- Lloyd <lloyd@rightup.co.uk> Tue, 30 Dec 2025 12:55:47 +0000

34
debian/control vendored Normal file
View File

@@ -0,0 +1,34 @@
Source: pymc-repeater
Section: net
Priority: optional
Maintainer: Lloyd <lloyd@rightup.co.uk>
Build-Depends: debhelper-compat (= 13),
dh-python,
python3-all,
python3-setuptools,
python3-setuptools-scm,
python3-wheel,
python3-pip,
python3-yaml,
python3-cherrypy3,
python3-paho-mqtt,
python3-psutil,
git
Standards-Version: 4.6.2
Homepage: https://github.com/rightup/pyMC_Repeater
X-Python3-Version: >= 3.8
Package: pymc-repeater
Architecture: all
Depends: ${python3:Depends},
${misc:Depends},
python3-yaml,
python3-cherrypy3,
python3-paho-mqtt,
python3-psutil,
python3-jwt
Description: PyMC Repeater Daemon
A mesh networking repeater daemon for LoRa devices.
.
This package provides the pymc-repeater service for managing
mesh network repeater functionality with a web interface.

1
debian/debhelper-build-stamp vendored Normal file
View File

@@ -0,0 +1 @@
pymc-repeater

3
debian/pymc-repeater.dirs vendored Normal file
View File

@@ -0,0 +1,3 @@
etc/pymc-repeater
var/log/pymc-repeater
usr/share/pymc-repeater

3
debian/pymc-repeater.install vendored Normal file
View File

@@ -0,0 +1,3 @@
config.yaml.example usr/share/pymc-repeater/
radio-presets.json usr/share/pymc-repeater/
radio-settings.json usr/share/pymc-repeater/

29
debian/pymc-repeater.postinst vendored Executable file
View File

@@ -0,0 +1,29 @@
#!/bin/sh
set -e
case "$1" in
configure)
# Create system user
if ! getent passwd pymc-repeater >/dev/null; then
adduser --system --group --home /var/lib/pymc-repeater \
--gecos "PyMC Repeater Service" pymc-repeater
fi
# Set permissions
chown -R pymc-repeater:pymc-repeater /etc/pymc-repeater
chown -R pymc-repeater:pymc-repeater /var/log/pymc-repeater
chmod 750 /etc/pymc-repeater
chmod 750 /var/log/pymc-repeater
# Copy example config if no config exists
if [ ! -f /etc/pymc-repeater/config.yaml ]; then
cp /usr/share/pymc-repeater/config.yaml.example /etc/pymc-repeater/config.yaml
chown pymc-repeater:pymc-repeater /etc/pymc-repeater/config.yaml
chmod 640 /etc/pymc-repeater/config.yaml
fi
;;
esac
#DEBHELPER#
exit 0

18
debian/pymc-repeater.postrm vendored Executable file
View File

@@ -0,0 +1,18 @@
#!/bin/sh
set -e
case "$1" in
purge)
# Remove user and directories
if getent passwd pymc-repeater >/dev/null; then
deluser --system pymc-repeater || true
fi
rm -rf /etc/pymc-repeater
rm -rf /var/log/pymc-repeater
rm -rf /var/lib/pymc-repeater
;;
esac
#DEBHELPER#
exit 0

15
debian/pymc-repeater.service vendored Normal file
View File

@@ -0,0 +1,15 @@
[Unit]
Description=PyMC Repeater Daemon
After=network.target
[Service]
Type=simple
User=pymc-repeater
Group=pymc-repeater
WorkingDirectory=/etc/pymc-repeater
ExecStart=/usr/bin/pymc-repeater
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target

22
debian/rules vendored Executable file
View File

@@ -0,0 +1,22 @@
#!/usr/bin/make -f
# -*- makefile -*-
export PYBUILD_NAME=pymc-repeater
export DH_VERBOSE=1
%:
dh $@ --with python3 --buildsystem=pybuild
override_dh_auto_test:
# Skip tests - cherrypy-cors not available in Debian repos
# Tests pass in development with: pip install cherrypy-cors
override_dh_auto_clean:
dh_auto_clean
rm -rf build/
rm -rf *.egg-info/
rm -rf .pybuild/
rm -f repeater/_version.py
override_dh_installsystemd:
dh_installsystemd --name=pymc-repeater

1
debian/source/format vendored Normal file
View File

@@ -0,0 +1 @@
3.0 (native)

View File

@@ -1,10 +1,10 @@
[build-system]
requires = ["setuptools>=61.0", "wheel"]
requires = ["setuptools>=61.0", "wheel", "setuptools_scm>=8.0"]
build-backend = "setuptools.build_meta"
[project]
name = "pymc_repeater"
version = "1.0.5"
dynamic = ["version"]
authors = [
{name = "Lloyd", email = "lloyd@rightup.co.uk"},
]
@@ -31,7 +31,7 @@ keywords = ["mesh", "networking", "lora", "repeater", "daemon", "iot"]
dependencies = [
"pymc_core[hardware] @ git+https://github.com/rightup/pyMC_core.git@feat/anon-req",
"pymc_core[hardware]>=1.0.6",
"pyyaml>=6.0.0",
"cherrypy>=18.0.0",
"paho-mqtt>=1.6.0",
@@ -73,3 +73,8 @@ target-version = ['py38', 'py39', 'py310', 'py311', 'py312']
[tool.isort]
profile = "black"
line_length = 100
[tool.setuptools_scm]
write_to = "repeater/_version.py"
version_scheme = "post-release"
local_scheme = "no-local-version"

View File

@@ -1 +1,8 @@
__version__ = "1.0.5-beta-3"
try:
from ._version import version as __version__
except ImportError:
try:
from importlib.metadata import version
__version__ = version("pymc_repeater")
except Exception:
__version__ = "unknown"

View File

@@ -0,0 +1,65 @@
import cherrypy
from functools import wraps
import logging
logger = logging.getLogger(__name__)
def require_auth(func):
@wraps(func)
def wrapper(*args, **kwargs):
# Skip authentication for OPTIONS requests (CORS preflight)
if cherrypy.request.method == 'OPTIONS':
return func(*args, **kwargs)
# Get auth handlers from global cherrypy config (not app config)
jwt_handler = cherrypy.config.get('jwt_handler')
token_manager = cherrypy.config.get('token_manager')
if not jwt_handler or not token_manager:
logger.error("Auth handlers not configured")
raise cherrypy.HTTPError(500, "Authentication not configured")
# Try JWT authentication first
auth_header = cherrypy.request.headers.get('Authorization', '')
if auth_header.startswith('Bearer '):
token = auth_header[7:] # Remove 'Bearer ' prefix
payload = jwt_handler.verify_jwt(token)
if payload:
# JWT is valid
cherrypy.request.user = {
'username': payload['sub'],
'client_id': payload['client_id'],
'auth_type': 'jwt'
}
return func(*args, **kwargs)
else:
logger.warning("Invalid or expired JWT token")
# Try API token authentication
api_key = cherrypy.request.headers.get('X-API-Key', '')
if api_key:
token_info = token_manager.verify_token(api_key)
if token_info:
# API token is valid
cherrypy.request.user = {
'username': 'api_token',
'token_name': token_info['name'],
'token_id': token_info['id'],
'auth_type': 'api_token'
}
return func(*args, **kwargs)
else:
logger.warning("Invalid API token")
# No valid authentication found
logger.warning(f"Unauthorized access attempt to {cherrypy.request.path_info}")
cherrypy.response.status = 401
cherrypy.response.headers['Content-Type'] = 'application/json'
return {'success': False, 'error': 'Unauthorized - Valid JWT or API token required'}
return wrapper

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -8,8 +8,8 @@
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans:wght@400;500;600;700&display=swap" rel="stylesheet">
<script type="module" crossorigin src="/assets/index-ViklARR2.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DiNHIYsR.css">
<script type="module" crossorigin src="/assets/index-DWtvChhM.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-Di_qhLFB.css">
</head>
<body>
<div id="app"></div>

134
scripts/build-dev.sh Executable file
View File

@@ -0,0 +1,134 @@
#!/bin/bash
# Build development .deb package for pyMC_Repeater
# Allows building from untagged commits with ~dev version suffix
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_step() {
echo -e "${BLUE}[STEP]${NC} $1"
}
# Change to project root
cd "$(dirname "$0")/.."
PROJECT_ROOT=$(pwd)
log_info "Building development .deb package for pyMC_Repeater..."
log_info "Project root: $PROJECT_ROOT"
# Check if we're in a git repository
if [ ! -d .git ]; then
log_error "Not in a git repository. Cannot use setuptools_scm."
exit 1
fi
# Get version from setuptools_scm
log_step "Detecting version from git..."
if ! command -v python3 &> /dev/null; then
log_error "python3 not found. Please install python3."
exit 1
fi
# Try to get version from setuptools_scm
VERSION=$(python3 -m setuptools_scm 2>/dev/null || echo "")
if [ -z "$VERSION" ]; then
log_error "Failed to get version from setuptools_scm."
log_error "Make sure setuptools_scm is installed: pip3 install setuptools_scm"
exit 1
fi
log_info "Detected version: $VERSION"
# Convert version to Debian format
# setuptools_scm format: 1.0.5.post3.dev0 or 1.0.5.dev3+g123abc
# Debian format: 1.0.5~dev3
DEBIAN_VERSION=$(echo "$VERSION" | sed -E 's/\.post([0-9]+).*$/~dev\1/' | sed -E 's/\.dev([0-9]+).*$/~dev\1/' | sed -E 's/\+.*$//')
log_info "Debian version: $DEBIAN_VERSION"
# Update debian/changelog
log_step "Updating debian/changelog..."
CHANGELOG_DATE=$(date -R)
cat > debian/changelog << EOF
pymc-repeater ($DEBIAN_VERSION) unstable; urgency=medium
* Development build from git commit $(git rev-parse --short HEAD)
* Version: $VERSION
-- Lloyd <lloyd@rightup.co.uk> $CHANGELOG_DATE
EOF
log_info "Changelog updated with version $DEBIAN_VERSION"
# Clean previous builds
log_step "Cleaning previous builds..."
rm -rf debian/pymc-repeater/
rm -rf debian/.debhelper/
rm -rf debian/files
rm -f debian/pymc-repeater.*.debhelper
rm -f debian/pymc-repeater.substvars
rm -f debian/*.log
rm -rf .pybuild/
rm -rf build/
rm -rf *.egg-info/
rm -f repeater/_version.py
rm -f ../*.deb
rm -f ../*.buildinfo
rm -f ../*.changes
# Build the package
log_step "Building .deb package..."
log_info "This may take a few minutes..."
# Build without signing (development builds don't need to be signed)
if debuild -us -uc -b 2>&1 | tee /tmp/debuild-dev.log; then
log_info "Build successful!"
else
log_error "Build failed. Check /tmp/debuild-dev.log for details."
exit 1
fi
# Find and display the built package
DEB_FILE=$(find .. -maxdepth 1 -name "pymc-repeater_${DEBIAN_VERSION}_*.deb" -type f | head -n 1)
if [ -n "$DEB_FILE" ]; then
log_info ""
log_info "════════════════════════════════════════════════════════════"
log_info "Build complete!"
log_info "Package: $(basename "$DEB_FILE")"
log_info "Location: $DEB_FILE"
log_info "Size: $(du -h "$DEB_FILE" | cut -f1)"
log_info "════════════════════════════════════════════════════════════"
log_info ""
log_info "To install:"
log_info " sudo dpkg -i $DEB_FILE"
log_info ""
log_info "To inspect package contents:"
log_info " dpkg-deb -c $DEB_FILE"
log_info ""
log_info "To check package info:"
log_info " dpkg-deb -I $DEB_FILE"
else
log_warn "Built package not found in expected location"
log_info "Searching for .deb files in parent directory..."
ls -lh ../*.deb 2>/dev/null || log_warn "No .deb files found"
fi

181
scripts/build-prod.sh Executable file
View File

@@ -0,0 +1,181 @@
#!/bin/bash
# Build production .deb package for pyMC_Repeater
# Requires a clean git tag - fails if not on a tagged commit
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_step() {
echo -e "${BLUE}[STEP]${NC} $1"
}
# Change to project root
cd "$(dirname "$0")/.."
PROJECT_ROOT=$(pwd)
log_info "Building production .deb package for pyMC_Repeater..."
log_info "Project root: $PROJECT_ROOT"
# Check if we're in a git repository
if [ ! -d .git ]; then
log_error "Not in a git repository. Cannot use setuptools_scm."
exit 1
fi
# Check for uncommitted changes
if ! git diff-index --quiet HEAD --; then
log_error "You have uncommitted changes. Production builds require a clean git state."
log_error "Please commit or stash your changes first."
exit 1
fi
# Check if current commit is tagged
log_step "Verifying git tag..."
CURRENT_COMMIT=$(git rev-parse HEAD)
TAG=$(git describe --exact-match --tags "$CURRENT_COMMIT" 2>/dev/null || echo "")
if [ -z "$TAG" ]; then
log_error "Current commit is not tagged."
log_error "Production builds require a git tag."
log_error ""
log_error "To create a tag:"
log_error " git tag v1.0.5"
log_error " git push origin v1.0.5"
log_error ""
log_error "For development builds, use: ./scripts/build-dev.sh"
exit 1
fi
log_info "Building from tag: $TAG"
# Get version from setuptools_scm
log_step "Detecting version from git tag..."
if ! command -v python3 &> /dev/null; then
log_error "python3 not found. Please install python3."
exit 1
fi
# Try to get version from setuptools_scm
VERSION=$(python3 -m setuptools_scm 2>/dev/null || echo "")
if [ -z "$VERSION" ]; then
log_error "Failed to get version from setuptools_scm."
log_error "Make sure setuptools_scm is installed: pip3 install setuptools_scm"
exit 1
fi
log_info "Detected version: $VERSION"
# Verify version doesn't contain dev/post markers (should be clean release version)
if echo "$VERSION" | grep -qE '(dev|post|\+)'; then
log_error "Version '$VERSION' contains development markers."
log_error "Production builds must be from a clean tag without uncommitted changes."
log_error "Current tag: $TAG"
log_error ""
log_error "This might happen if:"
log_error " - There are commits after the tag"
log_error " - The working directory is dirty"
log_error " - The tag format is not recognized"
exit 1
fi
# Use the version as-is for Debian (it should be clean like "1.0.5")
DEBIAN_VERSION="$VERSION"
log_info "Debian version: $DEBIAN_VERSION"
# Update debian/changelog with production release info
log_step "Updating debian/changelog..."
CHANGELOG_DATE=$(date -R)
cat > debian/changelog << EOF
pymc-repeater ($DEBIAN_VERSION) stable; urgency=medium
* Production release $VERSION
* Git tag: $TAG
* Commit: $(git rev-parse --short HEAD)
-- Lloyd <lloyd@rightup.co.uk> $CHANGELOG_DATE
EOF
log_info "Changelog updated with version $DEBIAN_VERSION"
# Clean previous builds
log_step "Cleaning previous builds..."
rm -rf debian/pymc-repeater/
rm -rf debian/.debhelper/
rm -rf debian/files
rm -f debian/pymc-repeater.*.debhelper
rm -f debian/pymc-repeater.substvars
rm -f debian/*.log
rm -rf .pybuild/
rm -rf build/
rm -rf *.egg-info/
rm -f repeater/_version.py
rm -f ../*.deb
rm -f ../*.buildinfo
rm -f ../*.changes
# Build the package
log_step "Building production .deb package..."
log_info "This may take a few minutes..."
# Build without signing (can be signed later if needed)
if debuild -us -uc -b 2>&1 | tee /tmp/debuild-prod.log; then
log_info "Build successful!"
else
log_error "Build failed. Check /tmp/debuild-prod.log for details."
exit 1
fi
# Find and display the built package
DEB_FILE=$(find .. -maxdepth 1 -name "pymc-repeater_${DEBIAN_VERSION}_*.deb" -type f | head -n 1)
if [ -n "$DEB_FILE" ]; then
# Run lintian to check package quality
log_step "Running lintian checks..."
lintian "$DEB_FILE" || log_warn "Lintian found some issues (non-fatal)"
log_info ""
log_info "════════════════════════════════════════════════════════════"
log_info "Production build complete!"
log_info "Package: $(basename "$DEB_FILE")"
log_info "Location: $DEB_FILE"
log_info "Size: $(du -h "$DEB_FILE" | cut -f1)"
log_info "Version: $VERSION"
log_info "Git tag: $TAG"
log_info "════════════════════════════════════════════════════════════"
log_info ""
log_info "To install:"
log_info " sudo dpkg -i $DEB_FILE"
log_info ""
log_info "To inspect package contents:"
log_info " dpkg-deb -c $DEB_FILE"
log_info ""
log_info "To check package info:"
log_info " dpkg-deb -I $DEB_FILE"
log_info ""
log_info "To sign the package:"
log_info " debsign $DEB_FILE"
else
log_warn "Built package not found in expected location"
log_info "Searching for .deb files in parent directory..."
ls -lh ../*.deb 2>/dev/null || log_warn "No .deb files found"
fi

81
scripts/setup-build-env.sh Executable file
View File

@@ -0,0 +1,81 @@
#!/bin/bash
# Setup Debian/Ubuntu build environment for pyMC_Repeater
# This script installs all required build dependencies using apt
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Check if running as root or with sudo
if [ "$EUID" -ne 0 ]; then
log_error "This script must be run with sudo or as root"
exit 1
fi
log_info "Setting up build environment for pyMC_Repeater..."
# Update package list
log_info "Updating package lists..."
apt-get update
# Install Debian packaging tools
log_info "Installing Debian packaging tools..."
apt-get install -y \
debhelper \
devscripts \
build-essential \
fakeroot \
lintian \
git
# Install Python build dependencies
log_info "Installing Python build dependencies..."
apt-get install -y \
dh-python \
python3-all \
python3-setuptools \
python3-setuptools-scm \
python3-wheel \
python3-pip \
python3-dev
# Install Python runtime dependencies (for building)
log_info "Installing Python runtime dependencies..."
apt-get install -y \
python3-yaml \
python3-cherrypy3 \
python3-paho-mqtt \
python3-psutil \
python3-jwt || {
log_warn "python3-jwt not available via apt, will be installed via pip during build"
}
# Note: cherrypy-cors is not available in Debian repos
# For development testing: pip install cherrypy-cors
# Clean up
log_info "Cleaning up..."
apt-get autoremove -y
apt-get clean
log_info "Build environment setup complete!"
log_info ""
log_info "Next steps:"
log_info " - Run './scripts/build-dev.sh' to build a development .deb package"
log_info " - Run './scripts/build-prod.sh' to build a production .deb package (requires git tag)"

10
setup.py Executable file
View File

@@ -0,0 +1,10 @@
#!/usr/bin/env python3
"""
Setup script for pymc_repeater
Version is managed by setuptools_scm from git tags
"""
from setuptools import setup
if __name__ == "__main__":
setup()