mirror of
https://github.com/Dispatcharr/Dispatcharr.git
synced 2026-01-23 02:35:14 +00:00
New files: .github/workflows/freebsd-release.yml: - Triggers on version tags (v*) and manual dispatch - Builds frontend with npm - Validates Python syntax - Runs compatibility tests - Creates release tarball with checksums - Tests package in FreeBSD VM (vmactions/freebsd-vm) - Uploads to GitHub Releases with release notes scripts/build-freebsd.sh: - Standalone build script for local builds - Works on FreeBSD, Linux, or macOS - Options: --version, --output, --skip-frontend, --skip-tests, --clean - Generates tarball with SHA256 and MD5 checksums - Self-contained with dependency checking BUILD.md: - Complete documentation for building FreeBSD packages - Quick start guide for automated and local builds - Package contents and installation instructions - Troubleshooting section - Development workflow guide The release package includes: - Pre-built React frontend - Django application code - FreeBSD installation script - Compatibility test suite - Build metadata
531 lines
14 KiB
Bash
Executable file
531 lines
14 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
#
|
|
# Dispatcharr FreeBSD Build Script
|
|
#
|
|
# Creates a release package for FreeBSD containing:
|
|
# - Pre-built frontend assets
|
|
# - Python application code
|
|
# - Installation scripts
|
|
# - RC.d service templates
|
|
#
|
|
# Usage:
|
|
# ./build-freebsd.sh [OPTIONS]
|
|
#
|
|
# Options:
|
|
# --version <ver> Version string (default: from version.py or 0.0.0-dev)
|
|
# --output <path> Output directory (default: ./build)
|
|
# --skip-frontend Skip frontend build (use existing dist/)
|
|
# --skip-tests Skip compatibility tests
|
|
# --clean Clean build directory before building
|
|
# --help Show this help message
|
|
#
|
|
# Requirements:
|
|
# - Node.js and npm (for frontend build)
|
|
# - Python 3.x (for syntax validation)
|
|
# - tar, gzip (for packaging)
|
|
#
|
|
# This script can run on FreeBSD, Linux, or macOS.
|
|
#
|
|
|
|
set -e
|
|
set -o pipefail
|
|
|
|
# Script directory and project root
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
|
|
# Default configuration
|
|
VERSION=""
|
|
OUTPUT_DIR="${PROJECT_ROOT}/build"
|
|
SKIP_FRONTEND=0
|
|
SKIP_TESTS=0
|
|
CLEAN_BUILD=0
|
|
PROJECT_NAME="dispatcharr"
|
|
|
|
# Colors (disabled if not a terminal)
|
|
if [ -t 1 ]; then
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
BOLD='\033[1m'
|
|
NC='\033[0m'
|
|
else
|
|
RED=''
|
|
GREEN=''
|
|
YELLOW=''
|
|
BLUE=''
|
|
BOLD=''
|
|
NC=''
|
|
fi
|
|
|
|
# Logging functions
|
|
log_info() {
|
|
echo -e "${BLUE}[INFO]${NC} $1"
|
|
}
|
|
|
|
log_success() {
|
|
echo -e "${GREEN}[OK]${NC} $1"
|
|
}
|
|
|
|
log_warn() {
|
|
echo -e "${YELLOW}[WARN]${NC} $1"
|
|
}
|
|
|
|
log_error() {
|
|
echo -e "${RED}[ERROR]${NC} $1" >&2
|
|
}
|
|
|
|
log_step() {
|
|
echo ""
|
|
echo -e "${BOLD}━━━ $1 ━━━${NC}"
|
|
}
|
|
|
|
show_help() {
|
|
cat << 'EOF'
|
|
Dispatcharr FreeBSD Build Script
|
|
|
|
USAGE:
|
|
./build-freebsd.sh [OPTIONS]
|
|
|
|
OPTIONS:
|
|
--version <ver> Version string to embed in package
|
|
Default: extracted from version.py or "0.0.0-dev"
|
|
|
|
--output <path> Output directory for build artifacts
|
|
Default: ./build
|
|
|
|
--skip-frontend Skip frontend build, use existing frontend/dist/
|
|
Useful for faster rebuilds when frontend hasn't changed
|
|
|
|
--skip-tests Skip FreeBSD compatibility tests
|
|
Not recommended for release builds
|
|
|
|
--clean Remove existing build directory before building
|
|
|
|
--help Show this help message
|
|
|
|
EXAMPLES:
|
|
# Standard build
|
|
./build-freebsd.sh
|
|
|
|
# Build specific version
|
|
./build-freebsd.sh --version 1.0.0
|
|
|
|
# Quick rebuild (skip frontend)
|
|
./build-freebsd.sh --skip-frontend
|
|
|
|
# Clean build to custom directory
|
|
./build-freebsd.sh --clean --output /tmp/dispatcharr-build
|
|
|
|
OUTPUT:
|
|
Creates a tarball: dispatcharr-<version>-freebsd.tar.gz
|
|
With checksums: dispatcharr-<version>-freebsd.tar.gz.sha256
|
|
dispatcharr-<version>-freebsd.tar.gz.md5
|
|
|
|
REQUIREMENTS:
|
|
- Node.js 18+ and npm (for frontend build)
|
|
- Python 3.9+ (for syntax validation)
|
|
- bash, tar, gzip, sha256sum/shasum
|
|
|
|
EOF
|
|
}
|
|
|
|
# Parse command line arguments
|
|
parse_args() {
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
--version)
|
|
VERSION="$2"
|
|
shift 2
|
|
;;
|
|
--output)
|
|
OUTPUT_DIR="$2"
|
|
shift 2
|
|
;;
|
|
--skip-frontend)
|
|
SKIP_FRONTEND=1
|
|
shift
|
|
;;
|
|
--skip-tests)
|
|
SKIP_TESTS=1
|
|
shift
|
|
;;
|
|
--clean)
|
|
CLEAN_BUILD=1
|
|
shift
|
|
;;
|
|
--help|-h)
|
|
show_help
|
|
exit 0
|
|
;;
|
|
*)
|
|
log_error "Unknown option: $1"
|
|
echo "Use --help for usage information"
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
# Check for required tools
|
|
check_dependencies() {
|
|
log_step "Checking Dependencies"
|
|
|
|
local missing=()
|
|
|
|
# Check Node.js (only if building frontend)
|
|
if [ $SKIP_FRONTEND -eq 0 ]; then
|
|
if command -v node >/dev/null 2>&1; then
|
|
log_success "Node.js $(node --version)"
|
|
else
|
|
missing+=("node")
|
|
fi
|
|
|
|
if command -v npm >/dev/null 2>&1; then
|
|
log_success "npm $(npm --version)"
|
|
else
|
|
missing+=("npm")
|
|
fi
|
|
fi
|
|
|
|
# Check Python
|
|
if command -v python3 >/dev/null 2>&1; then
|
|
log_success "Python $(python3 --version 2>&1 | cut -d' ' -f2)"
|
|
else
|
|
missing+=("python3")
|
|
fi
|
|
|
|
# Check tar
|
|
if command -v tar >/dev/null 2>&1; then
|
|
log_success "tar available"
|
|
else
|
|
missing+=("tar")
|
|
fi
|
|
|
|
# Check checksum tools
|
|
if command -v sha256sum >/dev/null 2>&1; then
|
|
SHA256_CMD="sha256sum"
|
|
log_success "sha256sum available"
|
|
elif command -v shasum >/dev/null 2>&1; then
|
|
SHA256_CMD="shasum -a 256"
|
|
log_success "shasum available"
|
|
else
|
|
missing+=("sha256sum or shasum")
|
|
fi
|
|
|
|
if command -v md5sum >/dev/null 2>&1; then
|
|
MD5_CMD="md5sum"
|
|
elif command -v md5 >/dev/null 2>&1; then
|
|
MD5_CMD="md5 -r"
|
|
else
|
|
MD5_CMD=""
|
|
log_warn "md5sum not available, skipping MD5 checksum"
|
|
fi
|
|
|
|
if [ ${#missing[@]} -gt 0 ]; then
|
|
log_error "Missing required tools: ${missing[*]}"
|
|
echo ""
|
|
echo "Please install the missing tools and try again."
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Determine version
|
|
determine_version() {
|
|
log_step "Determining Version"
|
|
|
|
if [ -n "$VERSION" ]; then
|
|
log_info "Using specified version: $VERSION"
|
|
return
|
|
fi
|
|
|
|
# Try to extract from version.py
|
|
if [ -f "${PROJECT_ROOT}/version.py" ]; then
|
|
VERSION=$(grep -oP "(?<=version = ['\"])[^'\"]*" "${PROJECT_ROOT}/version.py" 2>/dev/null || true)
|
|
if [ -n "$VERSION" ]; then
|
|
log_info "Extracted version from version.py: $VERSION"
|
|
return
|
|
fi
|
|
fi
|
|
|
|
# Try git tag
|
|
if command -v git >/dev/null 2>&1 && [ -d "${PROJECT_ROOT}/.git" ]; then
|
|
VERSION=$(git -C "${PROJECT_ROOT}" describe --tags --abbrev=0 2>/dev/null | sed 's/^v//' || true)
|
|
if [ -n "$VERSION" ]; then
|
|
log_info "Extracted version from git tag: $VERSION"
|
|
return
|
|
fi
|
|
fi
|
|
|
|
# Default version
|
|
VERSION="0.0.0-dev"
|
|
log_warn "Could not determine version, using: $VERSION"
|
|
}
|
|
|
|
# Build frontend
|
|
build_frontend() {
|
|
log_step "Building Frontend"
|
|
|
|
if [ $SKIP_FRONTEND -eq 1 ]; then
|
|
if [ -d "${PROJECT_ROOT}/frontend/dist" ]; then
|
|
log_info "Skipping frontend build (--skip-frontend)"
|
|
log_success "Using existing frontend/dist/"
|
|
return
|
|
else
|
|
log_error "frontend/dist/ does not exist. Cannot skip frontend build."
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
cd "${PROJECT_ROOT}/frontend"
|
|
|
|
log_info "Installing npm dependencies..."
|
|
npm ci --legacy-peer-deps 2>&1 | tail -5
|
|
|
|
log_info "Building frontend..."
|
|
npm run build 2>&1 | tail -10
|
|
|
|
if [ -d "dist" ]; then
|
|
log_success "Frontend built successfully"
|
|
log_info "Output size: $(du -sh dist | cut -f1)"
|
|
else
|
|
log_error "Frontend build failed - dist/ not created"
|
|
exit 1
|
|
fi
|
|
|
|
cd "${PROJECT_ROOT}"
|
|
}
|
|
|
|
# Validate Python code
|
|
validate_python() {
|
|
log_step "Validating Python Code"
|
|
|
|
cd "${PROJECT_ROOT}"
|
|
|
|
log_info "Checking Python syntax..."
|
|
local errors=0
|
|
|
|
# Check manage.py
|
|
if python3 -m py_compile manage.py 2>/dev/null; then
|
|
log_success "manage.py"
|
|
else
|
|
log_error "manage.py has syntax errors"
|
|
((errors++))
|
|
fi
|
|
|
|
# Check apps, core, dispatcharr directories
|
|
for dir in apps core dispatcharr; do
|
|
if [ -d "$dir" ]; then
|
|
local count=$(find "$dir" -name "*.py" | wc -l | tr -d ' ')
|
|
local failed=0
|
|
while IFS= read -r -d '' file; do
|
|
if ! python3 -m py_compile "$file" 2>/dev/null; then
|
|
log_error "Syntax error: $file"
|
|
((failed++))
|
|
fi
|
|
done < <(find "$dir" -name "*.py" -print0)
|
|
|
|
if [ $failed -eq 0 ]; then
|
|
log_success "${dir}/ (${count} files)"
|
|
else
|
|
((errors += failed))
|
|
fi
|
|
fi
|
|
done
|
|
|
|
if [ $errors -gt 0 ]; then
|
|
log_error "Python validation failed with $errors errors"
|
|
exit 1
|
|
fi
|
|
|
|
log_success "All Python files validated"
|
|
}
|
|
|
|
# Run compatibility tests
|
|
run_tests() {
|
|
log_step "Running FreeBSD Compatibility Tests"
|
|
|
|
if [ $SKIP_TESTS -eq 1 ]; then
|
|
log_warn "Skipping tests (--skip-tests)"
|
|
return
|
|
fi
|
|
|
|
cd "${PROJECT_ROOT}"
|
|
|
|
if [ -x "scripts/ci_test.sh" ]; then
|
|
if ./scripts/ci_test.sh; then
|
|
log_success "All compatibility tests passed"
|
|
else
|
|
log_error "Compatibility tests failed"
|
|
exit 1
|
|
fi
|
|
elif [ -x "tests/freebsd/test_freebsd_compat.sh" ]; then
|
|
if bash tests/freebsd/test_freebsd_compat.sh; then
|
|
log_success "All compatibility tests passed"
|
|
else
|
|
log_error "Compatibility tests failed"
|
|
exit 1
|
|
fi
|
|
else
|
|
log_warn "No test scripts found, skipping tests"
|
|
fi
|
|
}
|
|
|
|
# Create the release package
|
|
create_package() {
|
|
log_step "Creating Release Package"
|
|
|
|
PACKAGE_NAME="${PROJECT_NAME}-${VERSION}-freebsd"
|
|
BUILD_DIR="${OUTPUT_DIR}/${PACKAGE_NAME}"
|
|
|
|
# Clean if requested
|
|
if [ $CLEAN_BUILD -eq 1 ] && [ -d "${OUTPUT_DIR}" ]; then
|
|
log_info "Cleaning build directory..."
|
|
rm -rf "${OUTPUT_DIR}"
|
|
fi
|
|
|
|
# Create build directory
|
|
mkdir -p "${BUILD_DIR}"
|
|
|
|
log_info "Copying application code..."
|
|
|
|
# Copy Python application
|
|
cp -r "${PROJECT_ROOT}/apps" "${BUILD_DIR}/"
|
|
cp -r "${PROJECT_ROOT}/core" "${BUILD_DIR}/"
|
|
cp -r "${PROJECT_ROOT}/dispatcharr" "${BUILD_DIR}/"
|
|
cp "${PROJECT_ROOT}/manage.py" "${BUILD_DIR}/"
|
|
cp "${PROJECT_ROOT}/requirements.txt" "${BUILD_DIR}/"
|
|
[ -f "${PROJECT_ROOT}/version.py" ] && cp "${PROJECT_ROOT}/version.py" "${BUILD_DIR}/"
|
|
|
|
# Copy pre-built frontend
|
|
log_info "Copying frontend assets..."
|
|
mkdir -p "${BUILD_DIR}/frontend"
|
|
cp -r "${PROJECT_ROOT}/frontend/dist" "${BUILD_DIR}/frontend/"
|
|
cp "${PROJECT_ROOT}/frontend/package.json" "${BUILD_DIR}/frontend/"
|
|
|
|
# Copy FreeBSD-specific files
|
|
log_info "Copying FreeBSD files..."
|
|
cp "${PROJECT_ROOT}/freebsd_start.sh" "${BUILD_DIR}/"
|
|
cp -r "${PROJECT_ROOT}/scripts" "${BUILD_DIR}/"
|
|
|
|
# Copy tests
|
|
mkdir -p "${BUILD_DIR}/tests/freebsd"
|
|
cp -r "${PROJECT_ROOT}/tests/freebsd/"* "${BUILD_DIR}/tests/freebsd/" 2>/dev/null || true
|
|
|
|
# Copy documentation
|
|
log_info "Copying documentation..."
|
|
[ -f "${PROJECT_ROOT}/README.md" ] && cp "${PROJECT_ROOT}/README.md" "${BUILD_DIR}/"
|
|
[ -f "${PROJECT_ROOT}/LICENSE" ] && cp "${PROJECT_ROOT}/LICENSE" "${BUILD_DIR}/"
|
|
[ -f "${PROJECT_ROOT}/CHANGELOG.md" ] && cp "${PROJECT_ROOT}/CHANGELOG.md" "${BUILD_DIR}/"
|
|
[ -f "${PROJECT_ROOT}/BUILD.md" ] && cp "${PROJECT_ROOT}/BUILD.md" "${BUILD_DIR}/"
|
|
|
|
# Create BUILD_INFO
|
|
log_info "Creating build info..."
|
|
cat > "${BUILD_DIR}/BUILD_INFO" << EOF
|
|
Package: ${PACKAGE_NAME}
|
|
Version: ${VERSION}
|
|
Build Date: $(date -u +"%Y-%m-%d %H:%M:%S UTC")
|
|
Build Host: $(hostname)
|
|
Build OS: $(uname -s) $(uname -r)
|
|
Target: FreeBSD 14.x/15.x (amd64)
|
|
EOF
|
|
|
|
# Add git info if available
|
|
if command -v git >/dev/null 2>&1 && [ -d "${PROJECT_ROOT}/.git" ]; then
|
|
echo "Git Commit: $(git -C "${PROJECT_ROOT}" rev-parse HEAD 2>/dev/null || echo 'unknown')" >> "${BUILD_DIR}/BUILD_INFO"
|
|
echo "Git Branch: $(git -C "${PROJECT_ROOT}" rev-parse --abbrev-ref HEAD 2>/dev/null || echo 'unknown')" >> "${BUILD_DIR}/BUILD_INFO"
|
|
fi
|
|
|
|
# Create installation wrapper
|
|
log_info "Creating install wrapper..."
|
|
cat > "${BUILD_DIR}/install.sh" << 'EOF'
|
|
#!/bin/sh
|
|
# Dispatcharr FreeBSD Installation Wrapper
|
|
set -e
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
|
|
echo "=========================================="
|
|
echo "Dispatcharr FreeBSD Installation"
|
|
echo "=========================================="
|
|
cat "${SCRIPT_DIR}/BUILD_INFO"
|
|
echo "=========================================="
|
|
echo ""
|
|
|
|
if [ "$(id -u)" -ne 0 ]; then
|
|
echo "Error: This script must be run as root"
|
|
exit 1
|
|
fi
|
|
|
|
if [ "$(uname -s)" != "FreeBSD" ]; then
|
|
echo "Error: This package is for FreeBSD only"
|
|
exit 1
|
|
fi
|
|
|
|
exec "${SCRIPT_DIR}/freebsd_start.sh" "$@"
|
|
EOF
|
|
chmod +x "${BUILD_DIR}/install.sh"
|
|
chmod +x "${BUILD_DIR}/freebsd_start.sh"
|
|
|
|
# Create tarball
|
|
log_info "Creating tarball..."
|
|
cd "${OUTPUT_DIR}"
|
|
tar -czvf "${PACKAGE_NAME}.tar.gz" "${PACKAGE_NAME}" 2>&1 | tail -3
|
|
|
|
# Generate checksums
|
|
log_info "Generating checksums..."
|
|
${SHA256_CMD} "${PACKAGE_NAME}.tar.gz" > "${PACKAGE_NAME}.tar.gz.sha256"
|
|
if [ -n "$MD5_CMD" ]; then
|
|
${MD5_CMD} "${PACKAGE_NAME}.tar.gz" > "${PACKAGE_NAME}.tar.gz.md5"
|
|
fi
|
|
|
|
log_success "Package created successfully"
|
|
}
|
|
|
|
# Print summary
|
|
print_summary() {
|
|
echo ""
|
|
echo -e "${BOLD}══════════════════════════════════════════════════════════════${NC}"
|
|
echo -e "${BOLD} BUILD COMPLETE ${NC}"
|
|
echo -e "${BOLD}══════════════════════════════════════════════════════════════${NC}"
|
|
echo ""
|
|
echo " Package: ${PACKAGE_NAME}.tar.gz"
|
|
echo " Version: ${VERSION}"
|
|
echo " Location: ${OUTPUT_DIR}/"
|
|
echo ""
|
|
echo " Files created:"
|
|
ls -lh "${OUTPUT_DIR}/${PACKAGE_NAME}.tar.gz"*
|
|
echo ""
|
|
echo " Package size: $(du -sh "${OUTPUT_DIR}/${PACKAGE_NAME}.tar.gz" | cut -f1)"
|
|
echo ""
|
|
echo -e "${BOLD}══════════════════════════════════════════════════════════════${NC}"
|
|
echo ""
|
|
echo "To install on FreeBSD:"
|
|
echo ""
|
|
echo " tar -xzf ${PACKAGE_NAME}.tar.gz"
|
|
echo " cd ${PACKAGE_NAME}"
|
|
echo " sudo ./install.sh"
|
|
echo ""
|
|
}
|
|
|
|
# Main execution
|
|
main() {
|
|
echo ""
|
|
echo -e "${BOLD}Dispatcharr FreeBSD Build Script${NC}"
|
|
echo ""
|
|
|
|
parse_args "$@"
|
|
|
|
cd "${PROJECT_ROOT}"
|
|
|
|
check_dependencies
|
|
determine_version
|
|
build_frontend
|
|
validate_python
|
|
run_tests
|
|
create_package
|
|
print_summary
|
|
}
|
|
|
|
main "$@"
|