Files
meshcore-bot/docker-setup.sh
agessaman 9ca59fbcee chore: Improve docker-compose.yml and docker-setup.sh for better environment management
- Clarified comments in docker-compose.yml regarding build configuration and updated pull_policy to 'missing' for clearer intent.
- Enhanced docker-setup.sh to ensure .env file creation and updates are more robust, with improved error handling and verification steps for Docker image configuration.
2026-01-17 11:49:57 -08:00

374 lines
14 KiB
Bash
Executable File

#!/bin/bash
# Setup script for Docker deployment
# Creates necessary directories and copies example config
set -e
echo "Setting up meshcore-bot Docker environment..."
# Create data directories
echo "Creating data directories..."
mkdir -p data/{config,databases,logs,backups}
# Copy example config if config doesn't exist
if [ ! -f "data/config/config.ini" ]; then
if [ -f "config.ini.example" ]; then
echo "Copying config.ini.example to data/config/config.ini..."
cp config.ini.example data/config/config.ini
else
echo "⚠️ Warning: config.ini.example not found. Please create data/config/config.ini manually."
exit 1
fi
else
echo "✓ Config file already exists at data/config/config.ini"
fi
# Detect platform
PLATFORM=$(uname -s)
CONFIG_FILE="data/config/config.ini"
# Function to update config.ini using sed (works with both existing and new configs)
update_config() {
local section=$1
local key=$2
local value=$3
# Check if section exists
if ! grep -q "^\[$section\]" "$CONFIG_FILE"; then
echo "" >> "$CONFIG_FILE"
echo "[$section]" >> "$CONFIG_FILE"
fi
# Check if key exists in section (look for key= with optional spaces)
if grep -q "^$key[[:space:]]*=" "$CONFIG_FILE"; then
# Update existing key (use | as delimiter to avoid issues with / in paths)
if [[ "$PLATFORM" == "Darwin" ]]; then
sed -i '' "s|^$key[[:space:]]*=.*|$key = $value|" "$CONFIG_FILE"
else
sed -i "s|^$key[[:space:]]*=.*|$key = $value|" "$CONFIG_FILE"
fi
else
# Add new key after section header
if [[ "$PLATFORM" == "Darwin" ]]; then
sed -i '' "/^\[$section\]/a\\
$key = $value
" "$CONFIG_FILE"
else
sed -i "/^\[$section\]/a $key = $value" "$CONFIG_FILE"
fi
fi
}
# Update database and log paths for Docker
echo ""
echo "Updating config.ini for Docker paths..."
UPDATED_COUNT=0
# Update Bot database path
update_config "Bot" "db_path" "/data/databases/meshcore_bot.db"
echo " ✓ Updated [Bot] db_path"
((UPDATED_COUNT++))
# Update Logging file path
update_config "Logging" "log_file" "/data/logs/meshcore_bot.log"
echo " ✓ Updated [Logging] log_file"
((UPDATED_COUNT++))
# Update Web_Viewer database path (if section exists)
if grep -q "^\[Web_Viewer\]" "$CONFIG_FILE"; then
update_config "Web_Viewer" "db_path" "/data/databases/bot_data.db"
echo " ✓ Updated [Web_Viewer] db_path"
((UPDATED_COUNT++))
fi
# Update PacketCapture paths (if section exists)
if grep -q "^\[PacketCapture\]" "$CONFIG_FILE"; then
# Only update output_file if it's set to a relative path
CURRENT_OUTPUT=$(grep "^output_file[[:space:]]*=" "$CONFIG_FILE" 2>/dev/null | sed 's/^output_file[[:space:]]*=[[:space:]]*//' | tr -d ' ' || echo "")
if [ -n "$CURRENT_OUTPUT" ] && [[ ! "$CURRENT_OUTPUT" == /* ]]; then
# Relative path - update to logs directory
update_config "PacketCapture" "output_file" "/data/logs/packets.jsonl"
echo " ✓ Updated [PacketCapture] output_file"
((UPDATED_COUNT++))
fi
# Update private_key_path if it's a relative path
CURRENT_KEY=$(grep "^private_key_path[[:space:]]*=" "$CONFIG_FILE" 2>/dev/null | sed 's/^private_key_path[[:space:]]*=[[:space:]]*//' | tr -d ' ' || echo "")
if [ -n "$CURRENT_KEY" ] && [[ ! "$CURRENT_KEY" == /* ]]; then
# Relative path - update to config directory (read-only is fine for keys)
update_config "PacketCapture" "private_key_path" "/data/config/private_key"
echo " ✓ Updated [PacketCapture] private_key_path"
((UPDATED_COUNT++))
fi
fi
# Update MapUploader private_key_path (if section exists)
if grep -q "^\[MapUploader\]" "$CONFIG_FILE"; then
CURRENT_KEY=$(grep "^private_key_path[[:space:]]*=" "$CONFIG_FILE" 2>/dev/null | sed 's/^private_key_path[[:space:]]*=[[:space:]]*//' | tr -d ' ' || echo "")
if [ -n "$CURRENT_KEY" ] && [[ ! "$CURRENT_KEY" == /* ]]; then
# Relative path - update to config directory
update_config "MapUploader" "private_key_path" "/data/config/private_key"
echo " ✓ Updated [MapUploader] private_key_path"
((UPDATED_COUNT++))
fi
fi
echo ""
echo "✓ Updated $UPDATED_COUNT path(s) for Docker deployment"
# Try to detect serial device
echo ""
echo "Detecting serial devices..."
SERIAL_DEVICE=""
DOCKER_DEVICE_PATH=""
if [[ "$PLATFORM" == "Linux" ]]; then
# Linux: Prefer /dev/serial/by-id/ for stable device identification
if [ -d "/dev/serial/by-id" ] && [ -n "$(ls -A /dev/serial/by-id 2>/dev/null)" ]; then
# Look for common MeshCore device patterns (case-insensitive)
# Prioritize devices that might be MeshCore-related
DEVICE=$(ls /dev/serial/by-id/* 2>/dev/null | grep -iE "(meshcore|heltec|rak|ch340|cp210|ft232)" | head -1)
# If no specific match, take the first USB serial device
if [ -z "$DEVICE" ]; then
DEVICE=$(ls /dev/serial/by-id/* 2>/dev/null | grep -i "usb" | head -1)
fi
# Last resort: any serial device
if [ -z "$DEVICE" ]; then
DEVICE=$(ls /dev/serial/by-id/* 2>/dev/null | head -1)
fi
if [ -n "$DEVICE" ] && [ -e "$DEVICE" ]; then
SERIAL_DEVICE="$DEVICE"
# For Docker, we'll map to /dev/ttyUSB0 in container
DOCKER_DEVICE_PATH="/dev/ttyUSB0"
echo "✓ Found serial device (by-id): $SERIAL_DEVICE"
fi
fi
# Fallback to /dev/ttyUSB* or /dev/ttyACM* if by-id not found
if [ -z "$SERIAL_DEVICE" ]; then
# Try ttyUSB first (more common)
for dev in /dev/ttyUSB* /dev/ttyACM*; do
if [ -e "$dev" ]; then
SERIAL_DEVICE="$dev"
DOCKER_DEVICE_PATH="/dev/ttyUSB0"
echo "✓ Found serial device: $SERIAL_DEVICE"
break
fi
done
fi
# Update docker-compose.yml if device found (Linux only)
if [ -n "$SERIAL_DEVICE" ] && [ -f "docker-compose.yml" ]; then
echo "Updating docker-compose.yml with device mapping..."
# Check if devices section exists (commented or uncommented)
if grep -qE "^ #? devices:" docker-compose.yml; then
# Uncomment and update existing devices section
if [[ "$PLATFORM" == "Darwin" ]]; then
# Uncomment devices line
sed -i '' 's/^ # devices:/ devices:/' docker-compose.yml
# Update device path - find commented device line and replace
sed -i '' "s|^ # - /dev/.*| - $SERIAL_DEVICE:$DOCKER_DEVICE_PATH|" docker-compose.yml
# Also handle if already uncommented
sed -i '' "s|^ - /dev/.*| - $SERIAL_DEVICE:$DOCKER_DEVICE_PATH|" docker-compose.yml
else
# Uncomment devices line
sed -i 's/^ # devices:/ devices:/' docker-compose.yml
# Update device path - find commented device line and replace
sed -i "s|^ # - /dev/.*| - $SERIAL_DEVICE:$DOCKER_DEVICE_PATH|" docker-compose.yml
# Also handle if already uncommented
sed -i "s|^ - /dev/.*| - $SERIAL_DEVICE:$DOCKER_DEVICE_PATH|" docker-compose.yml
fi
else
# Add devices section after restart line
if [[ "$PLATFORM" == "Darwin" ]]; then
sed -i '' "/^ restart: unless-stopped/a\\
\\
# Device access for serial ports (Linux)\\
devices:\\
- $SERIAL_DEVICE:$DOCKER_DEVICE_PATH
" docker-compose.yml
else
sed -i "/^ restart: unless-stopped/a\\
\\
# Device access for serial ports (Linux)\\
devices:\\
- $SERIAL_DEVICE:$DOCKER_DEVICE_PATH
" docker-compose.yml
fi
fi
echo "✓ Updated docker-compose.yml with device: $SERIAL_DEVICE -> $DOCKER_DEVICE_PATH"
fi
elif [[ "$PLATFORM" == "Darwin" ]]; then
# macOS: Use /dev/cu.* devices
DEVICE=$(ls /dev/cu.usbmodem* /dev/cu.usbserial* 2>/dev/null | head -1)
if [ -n "$DEVICE" ]; then
SERIAL_DEVICE="$DEVICE"
echo "✓ Found serial device: $SERIAL_DEVICE"
echo " Note: Docker Desktop on macOS doesn't support device passthrough."
echo " Consider using TCP connection or running natively on macOS."
fi
fi
# Update config.ini with serial device if found
if [ -n "$SERIAL_DEVICE" ]; then
if [[ "$PLATFORM" == "Linux" ]] && [[ "$SERIAL_DEVICE" == /dev/serial/by-id/* ]]; then
# On Linux with by-id path, resolve to actual device for Docker compatibility
# Docker containers may not resolve symlinks correctly, so use the actual device
ACTUAL_DEVICE=$(readlink -f "$SERIAL_DEVICE" 2>/dev/null || echo "")
if [ -n "$ACTUAL_DEVICE" ] && [ -e "$ACTUAL_DEVICE" ]; then
# Use actual device path for better Docker compatibility
update_config "Connection" "serial_port" "$ACTUAL_DEVICE"
echo "✓ Updated config.ini with serial port: $ACTUAL_DEVICE"
echo " (resolved from $SERIAL_DEVICE)"
else
# Fallback to by-id path if resolution fails
update_config "Connection" "serial_port" "$SERIAL_DEVICE"
echo "✓ Updated config.ini with serial port: $SERIAL_DEVICE"
echo " ⚠️ Note: If Docker can't access this symlink, use the actual device path"
fi
elif [[ "$PLATFORM" == "Linux" ]]; then
# On Linux, use the device path directly
update_config "Connection" "serial_port" "$SERIAL_DEVICE"
echo "✓ Updated config.ini with serial port: $SERIAL_DEVICE"
else
# macOS: just note it, but user needs to handle differently
update_config "Connection" "serial_port" "$SERIAL_DEVICE"
echo "✓ Updated config.ini with serial port: $SERIAL_DEVICE"
echo " ⚠️ Remember: Docker Desktop on macOS can't access serial devices directly."
fi
else
echo "⚠️ No serial device detected. You may need to:"
echo " - Connect your MeshCore device"
echo " - Manually set serial_port in config.ini"
echo " - Or use TCP/BLE connection instead"
fi
# Detect git branch and set Docker image tag
echo ""
echo "Detecting git branch for Docker image tag..."
# Try to get current branch name
GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
DOCKER_IMAGE_TAG="latest"
DOCKER_IMAGE_REGISTRY="ghcr.io/agessaman/meshcore-bot"
if [ -n "$GIT_BRANCH" ]; then
# Map branch names to image tags
case "$GIT_BRANCH" in
main|master)
DOCKER_IMAGE_TAG="latest"
;;
*)
# Use branch name as tag (e.g., dev -> dev, feature/xyz -> feature-xyz)
DOCKER_IMAGE_TAG=$(echo "$GIT_BRANCH" | sed 's/[\/_]/-/g')
;;
esac
echo " ✓ Detected branch: $GIT_BRANCH -> image tag: $DOCKER_IMAGE_TAG"
else
echo " ⚠️ Not a git repository or branch detection failed, using 'latest'"
fi
# Create or update .env file for docker-compose
# Ensure this always runs, even if there were non-critical errors earlier
ENV_FILE=".env"
# Temporarily disable exit on error for .env creation
set +e
# Check if .env file exists and has the tag variable
if [ ! -f "$ENV_FILE" ]; then
# Create new .env file
cat > "$ENV_FILE" << EOF
# Docker image configuration (auto-generated by docker-setup.sh)
DOCKER_IMAGE_REGISTRY=$DOCKER_IMAGE_REGISTRY
DOCKER_IMAGE_TAG=$DOCKER_IMAGE_TAG
EOF
if [ $? -eq 0 ]; then
echo " ✓ Created .env file with image tag: $DOCKER_IMAGE_TAG"
else
echo " ⚠️ Warning: Failed to create .env file"
fi
elif ! grep -q "^DOCKER_IMAGE_TAG=" "$ENV_FILE" 2>/dev/null; then
# .env exists but doesn't have the tag - append it
{
echo ""
echo "# Docker image configuration (auto-generated by docker-setup.sh)"
echo "DOCKER_IMAGE_REGISTRY=$DOCKER_IMAGE_REGISTRY"
echo "DOCKER_IMAGE_TAG=$DOCKER_IMAGE_TAG"
} >> "$ENV_FILE"
if [ $? -eq 0 ]; then
echo " ✓ Added Docker image configuration to .env file with tag: $DOCKER_IMAGE_TAG"
else
echo " ⚠️ Warning: Failed to append to .env file"
fi
else
# Update existing .env file
if [[ "$PLATFORM" == "Darwin" ]]; then
sed -i '' "s|^DOCKER_IMAGE_TAG=.*|DOCKER_IMAGE_TAG=$DOCKER_IMAGE_TAG|" "$ENV_FILE" 2>/dev/null
sed -i '' "s|^DOCKER_IMAGE_REGISTRY=.*|DOCKER_IMAGE_REGISTRY=$DOCKER_IMAGE_REGISTRY|" "$ENV_FILE" 2>/dev/null
else
sed -i "s|^DOCKER_IMAGE_TAG=.*|DOCKER_IMAGE_TAG=$DOCKER_IMAGE_TAG|" "$ENV_FILE" 2>/dev/null
sed -i "s|^DOCKER_IMAGE_REGISTRY=.*|DOCKER_IMAGE_REGISTRY=$DOCKER_IMAGE_REGISTRY|" "$ENV_FILE" 2>/dev/null
fi
if [ $? -eq 0 ]; then
echo " ✓ Updated .env file with image tag: $DOCKER_IMAGE_TAG"
else
echo " ⚠️ Warning: Failed to update .env file"
fi
fi
# Re-enable exit on error
set -e
# Verify .env file was created/updated
if [ -f "$ENV_FILE" ] && grep -q "^DOCKER_IMAGE_TAG=" "$ENV_FILE" 2>/dev/null; then
ACTUAL_TAG=$(grep "^DOCKER_IMAGE_TAG=" "$ENV_FILE" | cut -d'=' -f2 | tr -d ' ')
echo " ✓ Verified .env file contains DOCKER_IMAGE_TAG=$ACTUAL_TAG"
else
echo " ⚠️ Warning: .env file may not have been created correctly"
echo " Creating .env file manually..."
cat > "$ENV_FILE" << EOF
# Docker image configuration (auto-generated by docker-setup.sh)
DOCKER_IMAGE_REGISTRY=$DOCKER_IMAGE_REGISTRY
DOCKER_IMAGE_TAG=$DOCKER_IMAGE_TAG
EOF
if [ -f "$ENV_FILE" ]; then
echo " ✓ Manually created .env file with tag: $DOCKER_IMAGE_TAG"
else
echo " ✗ Error: Could not create .env file. Please create it manually:"
echo " echo 'DOCKER_IMAGE_REGISTRY=$DOCKER_IMAGE_REGISTRY' > .env"
echo " echo 'DOCKER_IMAGE_TAG=$DOCKER_IMAGE_TAG' >> .env"
fi
fi
# Set permissions (container runs as UID 1000)
echo ""
echo "Setting permissions..."
chmod -R 755 data/
chown -R 1000:1000 data/ 2>/dev/null || echo "Note: Could not set ownership (may need sudo)"
echo ""
echo "✓ Setup complete!"
echo ""
echo "Next steps:"
if [ -z "$SERIAL_DEVICE" ]; then
echo "1. Connect your MeshCore device or configure TCP/BLE connection"
fi
echo "1. Review data/config/config.ini and adjust settings if needed"
echo "2. Build the Docker image (to avoid pull warnings):"
echo " docker compose build"
echo ""
echo "3. Start the container:"
echo " docker compose up -d"
echo ""
echo " Or build and start in one command:"
echo " docker compose up -d --build"
echo ""
echo "4. View logs:"
echo " docker compose logs -f"