- Update mitmproxy to v11.1.3 - Build from Python source (no prebuilt arm64 binaries) - Add Python dependencies - Add mitmproxy to local-build.sh and sync-openwrt-packages.sh - Set PKGARCH:=all for Python package Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2027 lines
66 KiB
Bash
Executable File
2027 lines
66 KiB
Bash
Executable File
#!/bin/bash
|
||
#
|
||
# local-build.sh - Local build script for SecuBox packages
|
||
# Replicates GitHub Actions workflows for local testing
|
||
#
|
||
# Usage:
|
||
# ./local-build.sh validate # Run validation only
|
||
# ./local-build.sh build # Build all packages (x86_64)
|
||
# ./local-build.sh build luci-app-system-hub # Build single package
|
||
# ./local-build.sh build secubox-core # Build SecuBox Core package
|
||
# ./local-build.sh build netifyd # Build netifyd DPI engine
|
||
# ./local-build.sh build --arch aarch64 # Build for specific architecture
|
||
# ./local-build.sh full # Validate + Build
|
||
#
|
||
|
||
set -e # Exit on error
|
||
|
||
# Colors for output
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
CYAN='\033[0;36m'
|
||
NC='\033[0m' # No Color
|
||
|
||
# Normalize important directories
|
||
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||
REPO_ROOT=$(cd "$SCRIPT_DIR/.." && pwd)
|
||
|
||
# Configuration
|
||
# Available versions: 25.12.0-rc1 (default), 24.10.5 (stable LTS), 23.05.5, SNAPSHOT
|
||
OPENWRT_VERSION="${OPENWRT_VERSION:-24.10.5}"
|
||
SDK_DIR="${SDK_DIR:-./sdk}"
|
||
BUILD_DIR="${BUILD_DIR:-./build}"
|
||
CACHE_DIR="${CACHE_DIR:-./cache}"
|
||
OPENWRT_DIR="${OPENWRT_DIR:-./openwrt}"
|
||
|
||
# Default architecture
|
||
ARCH="aarch64_cortex-a72"
|
||
ARCH_NAME="aarch64_cortex-a72"
|
||
SDK_PATH="mvebu/cortexa72"
|
||
|
||
# Device profiles for firmware building
|
||
declare -A DEVICE_PROFILES=(
|
||
["espressobin-v7"]="mvebu:cortexa53:globalscale_espressobin:ESPRESSObin V7 (1-2GB DDR4)"
|
||
["espressobin-ultra"]="mvebu:cortexa53:globalscale_espressobin-ultra:ESPRESSObin Ultra (PoE, WiFi)"
|
||
# ["sheeva64"]="mvebu:cortexa53:globalscale_sheeva64:Sheeva64 (Plug computer)" # Disabled
|
||
["mochabin"]="mvebu:cortexa72:globalscale_mochabin:MOCHAbin (Quad-core A72, 10G)"
|
||
["x86-64"]="x86:64:generic:x86_64 Generic PC"
|
||
)
|
||
|
||
# Packages that must be built in the OpenWrt buildroot (toolchain) instead of the SDK.
|
||
# These packages compile native code and need system libraries not available in SDK.
|
||
OPENWRT_ONLY_PACKAGES=(
|
||
"netifyd"
|
||
"crowdsec"
|
||
"secubox-app-crowdsec"
|
||
"secubox-app-netifyd"
|
||
"secubox-app-ndpid"
|
||
"secubox-app-nodogsplash"
|
||
"secubox-app-mitmproxy"
|
||
"mitmproxy"
|
||
"nodogsplash"
|
||
)
|
||
|
||
# Helper functions
|
||
|
||
is_openwrt_only_pkg() {
|
||
local target="$1"
|
||
for pkg in "${OPENWRT_ONLY_PACKAGES[@]}"; do
|
||
if [[ "$pkg" == "$target" ]]; then
|
||
return 0
|
||
fi
|
||
done
|
||
return 1
|
||
}
|
||
print_header() {
|
||
echo ""
|
||
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||
echo -e "${CYAN}$1${NC}"
|
||
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||
echo ""
|
||
}
|
||
|
||
print_success() {
|
||
echo -e "${GREEN}✅ $1${NC}"
|
||
}
|
||
|
||
print_error() {
|
||
echo -e "${RED}❌ $1${NC}"
|
||
}
|
||
|
||
print_warning() {
|
||
echo -e "${YELLOW}⚠️ $1${NC}"
|
||
}
|
||
|
||
print_info() {
|
||
echo -e "${BLUE}ℹ️ $1${NC}"
|
||
}
|
||
|
||
# Architecture mapping
|
||
set_architecture() {
|
||
case "$1" in
|
||
x86-64|x86_64)
|
||
ARCH="x86-64"
|
||
ARCH_NAME="x86_64"
|
||
SDK_PATH="x86/64"
|
||
;;
|
||
aarch64-cortex-a53|aarch64_cortex-a53)
|
||
ARCH="aarch64-cortex-a53"
|
||
ARCH_NAME="aarch64_cortex-a53"
|
||
SDK_PATH="mvebu/cortexa53"
|
||
;;
|
||
aarch64-cortex-a72|aarch64_cortex-a72)
|
||
ARCH="aarch64-cortex-a72"
|
||
ARCH_NAME="aarch64_cortex-a72"
|
||
SDK_PATH="mvebu/cortexa72"
|
||
;;
|
||
aarch64-generic|aarch64_generic)
|
||
ARCH="aarch64-generic"
|
||
ARCH_NAME="aarch64_generic"
|
||
SDK_PATH="armsr/armv8"
|
||
;;
|
||
mips-24kc|mips_24kc)
|
||
ARCH="mips-24kc"
|
||
ARCH_NAME="mips_24kc"
|
||
SDK_PATH="ath79/generic"
|
||
;;
|
||
mipsel-24kc|mipsel_24kc)
|
||
ARCH="mipsel-24kc"
|
||
ARCH_NAME="mipsel_24kc"
|
||
SDK_PATH="ramips/mt7621"
|
||
;;
|
||
*)
|
||
print_error "Unknown architecture: $1"
|
||
print_info "Supported architectures: x86-64, aarch64-cortex-a53, aarch64-cortex-a72, aarch64-generic, mips-24kc, mipsel-24kc"
|
||
exit 1
|
||
;;
|
||
esac
|
||
print_info "Architecture: $ARCH ($ARCH_NAME) - SDK: $SDK_PATH"
|
||
}
|
||
|
||
# Check dependencies
|
||
check_dependencies() {
|
||
print_header "Checking Dependencies"
|
||
|
||
local missing_deps=()
|
||
|
||
# Build tools
|
||
for cmd in make gcc g++ git wget curl tar xz jq ninja; do
|
||
if ! command -v "$cmd" &> /dev/null; then
|
||
missing_deps+=("$cmd")
|
||
fi
|
||
done
|
||
|
||
# Validation tools
|
||
for cmd in shellcheck node; do
|
||
if ! command -v "$cmd" &> /dev/null; then
|
||
print_warning "$cmd not found (optional, needed for validation)"
|
||
fi
|
||
done
|
||
|
||
if [ ${#missing_deps[@]} -gt 0 ]; then
|
||
print_error "Missing required dependencies: ${missing_deps[*]}"
|
||
echo ""
|
||
echo "Install them with:"
|
||
echo " sudo apt-get install -y build-essential clang flex bison g++ gawk \\"
|
||
echo " gcc-multilib g++-multilib gettext git libncurses5-dev \\"
|
||
echo " libssl-dev python3-setuptools python3-dev rsync \\"
|
||
echo " swig unzip zlib1g-dev file wget curl jq ninja-build"
|
||
echo ""
|
||
echo "For validation tools:"
|
||
echo " sudo apt-get install -y shellcheck nodejs"
|
||
exit 1
|
||
fi
|
||
|
||
print_success "All required dependencies found"
|
||
}
|
||
|
||
# Validation functions (from test-validate.yml)
|
||
validate_makefiles() {
|
||
print_header "Validating Makefiles"
|
||
|
||
local errors=0
|
||
|
||
# Validate luci-app-* packages
|
||
for makefile in ../luci-app-*/Makefile; do
|
||
if [[ -f "$makefile" ]]; then
|
||
local pkg=$(dirname "$makefile" | xargs basename)
|
||
echo " 🔍 Checking $pkg..."
|
||
|
||
# Required fields
|
||
local required_fields=("PKG_NAME" "PKG_VERSION" "PKG_RELEASE" "PKG_LICENSE")
|
||
|
||
for field in "${required_fields[@]}"; do
|
||
if ! grep -q "^${field}:=" "$makefile"; then
|
||
print_error "Missing: $field in $pkg"
|
||
errors=$((errors + 1))
|
||
fi
|
||
done
|
||
|
||
# Check for include statements
|
||
if ! grep -q "include.*luci.mk\|include.*package.mk" "$makefile"; then
|
||
print_error "Missing include statement in $pkg"
|
||
errors=$((errors + 1))
|
||
fi
|
||
fi
|
||
done
|
||
|
||
# Validate luci-theme-* packages
|
||
for makefile in ../luci-theme-*/Makefile; do
|
||
if [[ -f "$makefile" ]]; then
|
||
local pkg=$(dirname "$makefile" | xargs basename)
|
||
echo " 🔍 Checking $pkg..."
|
||
|
||
# Required fields
|
||
local required_fields=("PKG_NAME" "PKG_VERSION" "PKG_RELEASE" "PKG_LICENSE")
|
||
|
||
for field in "${required_fields[@]}"; do
|
||
if ! grep -q "^${field}:=" "$makefile"; then
|
||
print_error "Missing: $field in $pkg"
|
||
errors=$((errors + 1))
|
||
fi
|
||
done
|
||
|
||
# Check for include statements
|
||
if ! grep -q "include.*luci.mk\|include.*package.mk" "$makefile"; then
|
||
print_error "Missing include statement in $pkg"
|
||
errors=$((errors + 1))
|
||
fi
|
||
fi
|
||
done
|
||
|
||
if [[ $errors -gt 0 ]]; then
|
||
print_error "Found $errors Makefile errors"
|
||
return 1
|
||
fi
|
||
|
||
print_success "All Makefiles valid"
|
||
return 0
|
||
}
|
||
|
||
validate_json() {
|
||
print_header "Validating JSON Files"
|
||
|
||
local errors=0
|
||
|
||
while IFS= read -r jsonfile; do
|
||
echo " 🔍 Checking $(basename "$jsonfile")..."
|
||
if ! jq empty "$jsonfile" 2>/dev/null; then
|
||
print_error "Invalid JSON: $jsonfile"
|
||
errors=$((errors + 1))
|
||
fi
|
||
done < <(find .. -name "*.json" -type f ! -path "*/node_modules/*" ! -path "*/sdk/*" ! -path "*/build/*")
|
||
|
||
if [[ $errors -gt 0 ]]; then
|
||
print_error "Found $errors JSON errors"
|
||
return 1
|
||
fi
|
||
|
||
print_success "All JSON files valid"
|
||
return 0
|
||
}
|
||
|
||
validate_javascript() {
|
||
print_header "Validating JavaScript Files"
|
||
|
||
if ! command -v node &> /dev/null; then
|
||
print_warning "Node.js not found, skipping JavaScript validation"
|
||
return 0
|
||
fi
|
||
|
||
local errors=0
|
||
|
||
while IFS= read -r jsfile; do
|
||
echo " 🔍 Checking $(basename "$jsfile")..."
|
||
if ! node --check "$jsfile" 2>/dev/null; then
|
||
print_error "Syntax error in: $jsfile"
|
||
errors=$((errors + 1))
|
||
fi
|
||
done < <(find .. -name "*.js" -type f ! -path "*/node_modules/*" ! -path "*/sdk/*" ! -path "*/build/*")
|
||
|
||
if [[ $errors -gt 0 ]]; then
|
||
print_error "Found $errors JavaScript errors"
|
||
return 1
|
||
fi
|
||
|
||
print_success "All JavaScript files valid"
|
||
return 0
|
||
}
|
||
|
||
validate_shellscripts() {
|
||
print_header "Validating Shell Scripts"
|
||
|
||
if ! command -v shellcheck &> /dev/null; then
|
||
print_warning "shellcheck not found, skipping shell script validation"
|
||
return 0
|
||
fi
|
||
|
||
local warnings=0
|
||
|
||
# Check RPCD scripts
|
||
while IFS= read -r script; do
|
||
echo " 🔍 Checking $(basename "$script")..."
|
||
if ! shellcheck -s sh "$script" 2>/dev/null; then
|
||
warnings=$((warnings + 1))
|
||
fi
|
||
done < <(find .. -path "*/rpcd/*" -type f ! -path "*/sdk/*" ! -path "*/build/*" 2>/dev/null)
|
||
|
||
# Check init scripts
|
||
while IFS= read -r script; do
|
||
echo " 🔍 Checking $(basename "$script")..."
|
||
if ! shellcheck -s sh "$script" 2>/dev/null; then
|
||
warnings=$((warnings + 1))
|
||
fi
|
||
done < <(find .. -path "*/init.d/*" -type f ! -path "*/sdk/*" ! -path "*/build/*" 2>/dev/null)
|
||
|
||
if [[ $warnings -gt 0 ]]; then
|
||
print_warning "Found $warnings shellcheck warnings (non-blocking)"
|
||
fi
|
||
|
||
print_success "Shell script validation complete"
|
||
return 0
|
||
}
|
||
|
||
check_file_permissions() {
|
||
print_header "Checking File Permissions"
|
||
|
||
local errors=0
|
||
|
||
# RPCD scripts should be executable
|
||
while IFS= read -r script; do
|
||
if [[ ! -x "$script" ]]; then
|
||
print_warning "Not executable: $script (fixing...)"
|
||
chmod +x "$script"
|
||
errors=$((errors + 1))
|
||
fi
|
||
done < <(find .. -path "*/usr/libexec/rpcd/*" -type f ! -path "*/sdk/*" ! -path "*/build/*" 2>/dev/null)
|
||
|
||
# Init scripts should be executable
|
||
while IFS= read -r script; do
|
||
if [[ ! -x "$script" ]]; then
|
||
print_warning "Not executable: $script (fixing...)"
|
||
chmod +x "$script"
|
||
errors=$((errors + 1))
|
||
fi
|
||
done < <(find .. -path "*/etc/init.d/*" -type f ! -path "*/sdk/*" ! -path "*/build/*" 2>/dev/null)
|
||
|
||
if [[ $errors -gt 0 ]]; then
|
||
print_warning "Fixed $errors permission issues"
|
||
fi
|
||
|
||
print_success "File permissions checked"
|
||
return 0
|
||
}
|
||
|
||
# Download and setup SDK
|
||
download_sdk() {
|
||
print_header "Downloading OpenWrt SDK"
|
||
|
||
local base_url="https://downloads.openwrt.org/releases/${OPENWRT_VERSION}/targets/${SDK_PATH}"
|
||
|
||
print_info "OpenWrt version: $OPENWRT_VERSION"
|
||
print_info "Architecture: $ARCH"
|
||
print_info "SDK URL: $base_url"
|
||
|
||
# Check cache
|
||
if [[ -d "$SDK_DIR" && -f "$SDK_DIR/.sdk_ready" ]]; then
|
||
local cached_version=$(cat "$SDK_DIR/.sdk_ready")
|
||
if [[ "$cached_version" == "${OPENWRT_VERSION}-${ARCH}" ]]; then
|
||
print_success "Using cached SDK: ${OPENWRT_VERSION}-${ARCH}"
|
||
return 0
|
||
else
|
||
print_info "Cached SDK version mismatch, re-downloading..."
|
||
rm -rf "$SDK_DIR"
|
||
fi
|
||
fi
|
||
|
||
# Find SDK filename
|
||
echo " 📥 Fetching SDK list..."
|
||
local sdk_file
|
||
sdk_file=$(curl -sL --retry 3 --retry-delay 5 "$base_url/" | grep -oP 'openwrt-sdk[^"<>]+\.tar\.(xz|zst)' | head -1)
|
||
|
||
if [[ -z "$sdk_file" ]]; then
|
||
print_error "Could not find SDK at $base_url"
|
||
return 1
|
||
fi
|
||
|
||
print_info "Downloading: $sdk_file"
|
||
|
||
# Download SDK
|
||
mkdir -p "$CACHE_DIR"
|
||
local sdk_archive="$CACHE_DIR/$sdk_file"
|
||
|
||
if [[ ! -f "$sdk_archive" ]]; then
|
||
echo " Downloading SDK (this may take several minutes)..."
|
||
if wget --retry-connrefused --waitretry=5 --timeout=60 --progress=dot:mega \
|
||
"${base_url}/${sdk_file}" -O "$sdk_archive" 2>&1 | grep --line-buffered '%'; then
|
||
print_success "Download complete"
|
||
else
|
||
# Fallback for older wget versions
|
||
wget --retry-connrefused --waitretry=5 --timeout=60 \
|
||
"${base_url}/${sdk_file}" -O "$sdk_archive"
|
||
fi
|
||
else
|
||
print_info "Using cached archive: $sdk_file"
|
||
fi
|
||
|
||
# Extract SDK
|
||
print_info "Extracting SDK..."
|
||
rm -rf "$SDK_DIR"
|
||
mkdir -p "$SDK_DIR"
|
||
tar -xf "$sdk_archive" -C "$SDK_DIR" --strip-components=1
|
||
|
||
# Mark SDK as ready
|
||
echo "${OPENWRT_VERSION}-${ARCH}" > "$SDK_DIR/.sdk_ready"
|
||
|
||
print_success "SDK downloaded and extracted"
|
||
return 0
|
||
}
|
||
|
||
# Setup SDK feeds
|
||
setup_sdk_feeds() {
|
||
print_header "Setting up SDK Feeds"
|
||
|
||
cd "$SDK_DIR"
|
||
|
||
# Remove unwanted feeds from feeds.conf.default
|
||
if [[ -f "feeds.conf.default" ]]; then
|
||
sed -i '/telephony/d' feeds.conf.default
|
||
sed -i '/routing/d' feeds.conf.default
|
||
print_success "Removed telephony and routing from feeds.conf.default"
|
||
fi
|
||
|
||
# Create local feed for SecuBox packages outside of SDK
|
||
local local_feed_dir="$(pwd)/../local-feed"
|
||
mkdir -p "$local_feed_dir"
|
||
|
||
# Determine correct branch based on OpenWrt version
|
||
local branch
|
||
if [[ "$OPENWRT_VERSION" == "SNAPSHOT" ]]; then
|
||
branch="master"
|
||
elif [[ "$OPENWRT_VERSION" =~ ^25\. ]]; then
|
||
branch="openwrt-25.12"
|
||
elif [[ "$OPENWRT_VERSION" =~ ^24\. ]]; then
|
||
branch="openwrt-24.10"
|
||
elif [[ "$OPENWRT_VERSION" =~ ^23\. ]]; then
|
||
branch="openwrt-23.05"
|
||
else
|
||
branch="openwrt-23.05" # fallback
|
||
fi
|
||
|
||
print_info "Using branch: $branch for OpenWrt $OPENWRT_VERSION"
|
||
|
||
# Use GitHub mirrors + local feed
|
||
cat > feeds.conf << FEEDS
|
||
src-git packages https://github.com/openwrt/packages.git;$branch
|
||
src-git luci https://github.com/openwrt/luci.git;$branch
|
||
src-link secubox $local_feed_dir
|
||
FEEDS
|
||
|
||
print_info "feeds.conf configured with local SecuBox feed at $local_feed_dir"
|
||
|
||
# Update feeds
|
||
echo "🔄 Updating feeds..."
|
||
local feeds_ok=0
|
||
local required_feeds=3
|
||
|
||
for feed in packages luci secubox; do
|
||
echo ""
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
echo "Updating feed: $feed"
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
|
||
local feed_success=0
|
||
for attempt in 1 2 3; do
|
||
echo "Attempt $attempt of 3..."
|
||
if ./scripts/feeds update "$feed" 2>&1 | tee "feed-update-${feed}.log"; then
|
||
if [[ -d "feeds/$feed" ]]; then
|
||
print_success "$feed updated successfully"
|
||
feeds_ok=$((feeds_ok + 1))
|
||
feed_success=1
|
||
break
|
||
else
|
||
print_warning "Feed directory not created, retrying..."
|
||
fi
|
||
else
|
||
print_warning "Update command failed, retrying..."
|
||
fi
|
||
sleep $((10 * attempt))
|
||
done
|
||
|
||
if [[ $feed_success -eq 0 ]]; then
|
||
print_error "Failed to update $feed after 3 attempts"
|
||
return 1
|
||
fi
|
||
done
|
||
|
||
echo ""
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
echo "📊 Feeds Status: $feeds_ok/$required_feeds updated"
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
|
||
if [[ $feeds_ok -lt $required_feeds ]]; then
|
||
print_error "Not all required feeds were updated successfully"
|
||
return 1
|
||
fi
|
||
|
||
# Install feeds
|
||
echo ""
|
||
echo "📦 Installing feeds..."
|
||
if ! ./scripts/feeds install -a 2>&1 | tee feed-install.log; then
|
||
print_warning "Feed installation had errors, checking if critical..."
|
||
fi
|
||
|
||
# Note: We skip Lua header installation and manual dependency builds
|
||
# Our SecuBox packages are PKGARCH:=all (scripts only) - no compilation needed
|
||
# lucihttp and cgi-io dependencies will be disabled in .config
|
||
echo ""
|
||
echo "ℹ️ Dependencies will be handled via .config (pre-built packages preferred)"
|
||
echo " Our packages are PKGARCH:=all (scripts) - no lucihttp compilation needed"
|
||
|
||
# Verify feeds
|
||
echo ""
|
||
echo "🔍 Verifying feed installation..."
|
||
for feed in packages luci secubox; do
|
||
if [[ -d "feeds/$feed" ]]; then
|
||
local feed_size=$(du -sh "feeds/$feed" 2>/dev/null | cut -f1)
|
||
print_success "feeds/$feed exists ($feed_size)"
|
||
else
|
||
if [[ "$feed" == "secubox" ]]; then
|
||
print_warning "feeds/$feed is empty (will be populated)"
|
||
else
|
||
print_error "feeds/$feed is missing!"
|
||
return 1
|
||
fi
|
||
fi
|
||
done
|
||
|
||
# Verify luci.mk
|
||
if [[ ! -f "feeds/luci/luci.mk" ]]; then
|
||
print_warning "luci.mk not found, creating fallback..."
|
||
mkdir -p feeds/luci
|
||
cat > feeds/luci/luci.mk << 'LUCI_MK'
|
||
# Minimal LuCI build system fallback
|
||
LUCI_PKGARCH:=all
|
||
|
||
define Package/Default
|
||
SECTION:=luci
|
||
CATEGORY:=LuCI
|
||
SUBMENU:=3. Applications
|
||
PKGARCH:=all
|
||
endef
|
||
LUCI_MK
|
||
fi
|
||
|
||
# Final cleanup
|
||
rm -f feeds/telephony.index feeds/routing.index 2>/dev/null || true
|
||
rm -rf feeds/telephony feeds/routing 2>/dev/null || true
|
||
|
||
make defconfig 2>/dev/null
|
||
|
||
cd - > /dev/null
|
||
|
||
print_success "SDK feeds configured"
|
||
return 0
|
||
}
|
||
|
||
# Copy packages to SDK feed
|
||
copy_packages() {
|
||
local single_package="$1"
|
||
|
||
print_header "Copying Packages to SecuBox Feed"
|
||
|
||
cd "$SDK_DIR"
|
||
|
||
# Use the local feed directory (outside SDK)
|
||
local feed_dir="../local-feed"
|
||
mkdir -p "$feed_dir"
|
||
local -a core_pkg_names=()
|
||
|
||
if [[ -n "$single_package" ]]; then
|
||
print_info "Copying single package: $single_package"
|
||
|
||
# Check in root directory first (luci-app-*, luci-theme-*)
|
||
if [[ -d "../../$single_package" && -f "../../${single_package}/Makefile" ]]; then
|
||
echo " 📁 $single_package"
|
||
cp -r "../../$single_package" "$feed_dir/"
|
||
|
||
# Fix Makefile include path for LuCI packages
|
||
if [[ "$single_package" =~ ^luci- ]]; then
|
||
sed -i 's|include.*luci\.mk|include $(TOPDIR)/feeds/luci/luci.mk|' "$feed_dir/$single_package/Makefile"
|
||
echo " ✓ Fixed Makefile include path"
|
||
fi
|
||
# Check in package/secubox/ directory (secubox-app-*, secubox-*)
|
||
elif [[ -d "../../package/secubox/$single_package" && -f "../../package/secubox/${single_package}/Makefile" ]]; then
|
||
echo " 📦 $single_package"
|
||
cp -r "../../package/secubox/$single_package" "$feed_dir/"
|
||
core_pkg_names+=("$single_package")
|
||
|
||
# Fix Makefile include paths for feed structure
|
||
if grep -q "../../lang/golang/golang-package.mk" "$feed_dir/$single_package/Makefile" 2>/dev/null; then
|
||
sed -i 's|include.*../../lang/golang/golang-package.mk|include $(TOPDIR)/feeds/packages/lang/golang/golang-package.mk|' "$feed_dir/$single_package/Makefile"
|
||
echo " ✓ Fixed golang package include path"
|
||
fi
|
||
else
|
||
print_error "Package $single_package not found or missing Makefile"
|
||
cd - > /dev/null
|
||
return 1
|
||
fi
|
||
else
|
||
print_info "Copying all packages"
|
||
|
||
# Copy luci-app-* packages
|
||
for pkg in ../../luci-app-*/; do
|
||
if [[ -d "$pkg" && -f "${pkg}Makefile" ]]; then
|
||
local pkg_name=$(basename "$pkg")
|
||
echo " 📁 $pkg_name"
|
||
cp -r "$pkg" "$feed_dir/"
|
||
|
||
# Fix Makefile include path for feed structure
|
||
sed -i 's|include.*luci\.mk|include $(TOPDIR)/feeds/luci/luci.mk|' "$feed_dir/$pkg_name/Makefile"
|
||
echo " ✓ Fixed Makefile include path"
|
||
fi
|
||
done
|
||
|
||
# Copy luci-theme-* packages
|
||
for pkg in ../../luci-theme-*/; do
|
||
if [[ -d "$pkg" && -f "${pkg}Makefile" ]]; then
|
||
local pkg_name=$(basename "$pkg")
|
||
echo " 📁 $pkg_name"
|
||
cp -r "$pkg" "$feed_dir/"
|
||
|
||
# Fix Makefile include path for feed structure
|
||
sed -i 's|include.*luci\.mk|include $(TOPDIR)/feeds/luci/luci.mk|' "$feed_dir/$pkg_name/Makefile"
|
||
echo " ✓ Fixed Makefile include path"
|
||
fi
|
||
done
|
||
|
||
# Copy luci-app-* packages from package/secubox/ (e.g., luci-app-secubox-admin)
|
||
for pkg in ../../package/secubox/luci-app-*/; do
|
||
if [[ -d "$pkg" && -f "${pkg}Makefile" ]]; then
|
||
local pkg_name=$(basename "$pkg")
|
||
echo " 📁 $pkg_name (SecuBox LuCI)"
|
||
cp -r "$pkg" "$feed_dir/"
|
||
|
||
# Fix Makefile include path for feed structure
|
||
sed -i 's|include.*luci\.mk|include $(TOPDIR)/feeds/luci/luci.mk|' "$feed_dir/$pkg_name/Makefile"
|
||
echo " ✓ Fixed Makefile include path"
|
||
fi
|
||
done
|
||
|
||
# Copy secubox-app-* packages (backend services)
|
||
for pkg in ../../package/secubox/secubox-app-*/; do
|
||
if [[ -d "$pkg" && -f "${pkg}Makefile" ]]; then
|
||
local pkg_name=$(basename "$pkg")
|
||
echo " 📦 $pkg_name (SecuBox App)"
|
||
cp -r "$pkg" "$feed_dir/"
|
||
core_pkg_names+=("$pkg_name")
|
||
|
||
# Fix Makefile include paths for feed structure
|
||
if grep -q "../../lang/golang/golang-package.mk" "$feed_dir/$pkg_name/Makefile" 2>/dev/null; then
|
||
sed -i 's|include.*../../lang/golang/golang-package.mk|include $(TOPDIR)/feeds/packages/lang/golang/golang-package.mk|' "$feed_dir/$pkg_name/Makefile"
|
||
echo " ✓ Fixed golang package include path"
|
||
fi
|
||
fi
|
||
done
|
||
|
||
# Copy other core packages (non-LuCI, non-secubox-app)
|
||
for pkg in ../../package/secubox/*/; do
|
||
if [[ -d "$pkg" && -f "${pkg}Makefile" ]]; then
|
||
local pkg_name=$(basename "$pkg")
|
||
# Skip if already copied (luci-app-*, luci-theme-*, secubox-app-*)
|
||
if [[ ! "$pkg_name" =~ ^luci-app- ]] && \
|
||
[[ ! "$pkg_name" =~ ^luci-theme- ]] && \
|
||
[[ ! "$pkg_name" =~ ^secubox-app- ]] && \
|
||
[[ "$pkg_name" != ".appstore" ]]; then
|
||
echo " 📁 $pkg_name (Core)"
|
||
cp -r "$pkg" "$feed_dir/"
|
||
core_pkg_names+=("$pkg_name")
|
||
fi
|
||
fi
|
||
done
|
||
fi
|
||
|
||
echo ""
|
||
print_info "Packages in feed:"
|
||
ls -d "$feed_dir/luci-app-"*/ 2>/dev/null || true
|
||
ls -d "$feed_dir/luci-theme-"*/ 2>/dev/null || true
|
||
ls -d "$feed_dir/secubox-app-"*/ 2>/dev/null || true
|
||
|
||
# Update the secubox feed
|
||
echo ""
|
||
echo "🔄 Updating SecuBox feed index..."
|
||
./scripts/feeds update secubox
|
||
|
||
# Install packages from secubox feed
|
||
echo ""
|
||
echo "📦 Installing packages from SecuBox feed..."
|
||
if [[ -n "$single_package" ]]; then
|
||
echo " Installing $single_package..."
|
||
./scripts/feeds install "$single_package"
|
||
else
|
||
# Install luci-app-* packages
|
||
for pkg in "$feed_dir"/luci-app-*/; do
|
||
if [[ -d "$pkg" ]]; then
|
||
local pkg_name=$(basename "$pkg")
|
||
echo " Installing $pkg_name..."
|
||
./scripts/feeds install "$pkg_name" 2>&1 | grep -v "WARNING:" || true
|
||
fi
|
||
done
|
||
|
||
# Install luci-theme-* packages
|
||
for pkg in "$feed_dir"/luci-theme-*/; do
|
||
if [[ -d "$pkg" ]]; then
|
||
local pkg_name=$(basename "$pkg")
|
||
echo " Installing $pkg_name..."
|
||
./scripts/feeds install "$pkg_name" 2>&1 | grep -v "WARNING:" || true
|
||
fi
|
||
done
|
||
|
||
# Install secubox-app-* packages
|
||
for pkg in "$feed_dir"/secubox-app-*/; do
|
||
if [[ -d "$pkg" ]]; then
|
||
local pkg_name=$(basename "$pkg")
|
||
echo " Installing $pkg_name..."
|
||
./scripts/feeds install "$pkg_name" 2>&1 | grep -v "WARNING:" || true
|
||
fi
|
||
done
|
||
|
||
# Install secubox core packages
|
||
for pkg_name in "${core_pkg_names[@]}"; do
|
||
local pkg_path="$feed_dir/$pkg_name"
|
||
if [[ -d "$pkg_path" ]]; then
|
||
echo " Installing $pkg_name..."
|
||
# For netifyd, ensure we're using SecuBox feed (not packages feed which has old version)
|
||
if [[ "$pkg_name" == "netifyd" ]]; then
|
||
./scripts/feeds uninstall netifyd 2>&1 | grep -v "WARNING:" || true
|
||
./scripts/feeds install -p secubox netifyd 2>&1 | grep -v "WARNING:" || true
|
||
else
|
||
./scripts/feeds install "$pkg_name" 2>&1 | grep -v "WARNING:" || true
|
||
fi
|
||
fi
|
||
done
|
||
fi
|
||
|
||
cd - > /dev/null
|
||
|
||
print_success "Packages copied and installed to feed"
|
||
return 0
|
||
}
|
||
|
||
# Configure packages
|
||
configure_packages() {
|
||
local single_package="$1"
|
||
|
||
print_header "Configuring Packages"
|
||
|
||
cd "$SDK_DIR"
|
||
|
||
echo "⚙️ Enabling packages..."
|
||
|
||
if [[ -n "$single_package" ]]; then
|
||
# Enable only the specified package
|
||
if [[ -d "feeds/secubox/$single_package" ]]; then
|
||
echo "CONFIG_PACKAGE_${single_package}=m" >> .config
|
||
print_success "$single_package enabled"
|
||
else
|
||
print_error "Package $single_package not found in feed"
|
||
cd - > /dev/null
|
||
return 1
|
||
fi
|
||
else
|
||
# Enable all SecuBox packages from feed (luci-app-*)
|
||
for pkg in feeds/secubox/luci-app-*/; do
|
||
if [[ -d "$pkg" ]]; then
|
||
local pkg_name=$(basename "$pkg")
|
||
echo "CONFIG_PACKAGE_${pkg_name}=m" >> .config
|
||
print_success "$pkg_name enabled"
|
||
fi
|
||
done
|
||
|
||
# Enable all SecuBox theme packages from feed (luci-theme-*)
|
||
for pkg in feeds/secubox/luci-theme-*/; do
|
||
if [[ -d "$pkg" ]]; then
|
||
local pkg_name=$(basename "$pkg")
|
||
echo "CONFIG_PACKAGE_${pkg_name}=m" >> .config
|
||
print_success "$pkg_name enabled"
|
||
fi
|
||
done
|
||
|
||
# Enable all SecuBox app packages from feed (secubox-app-*)
|
||
for pkg in feeds/secubox/secubox-app-*/; do
|
||
if [[ -d "$pkg" ]]; then
|
||
local pkg_name=$(basename "$pkg")
|
||
echo "CONFIG_PACKAGE_${pkg_name}=m" >> .config
|
||
print_success "$pkg_name enabled"
|
||
fi
|
||
done
|
||
fi
|
||
|
||
# Disable problematic packages that fail to compile in SDK
|
||
# Our SecuBox packages are PKGARCH:=all (scripts) so they don't need these
|
||
echo ""
|
||
echo "⚠️ Disabling packages that fail in SDK environment..."
|
||
echo "# CONFIG_PACKAGE_lucihttp is not set" >> .config
|
||
echo "# CONFIG_PACKAGE_cgi-io is not set" >> .config
|
||
print_info "lucihttp and cgi-io disabled (fail to compile: missing lua.h)"
|
||
|
||
# Enable use of pre-built packages from feeds
|
||
echo "CONFIG_DEVEL=y" >> .config
|
||
echo "CONFIG_AUTOREBUILD=y" >> .config
|
||
echo "CONFIG_AUTOREMOVE=y" >> .config
|
||
echo "CONFIG_FEED_packages=y" >> .config
|
||
echo "CONFIG_FEED_luci=y" >> .config
|
||
|
||
make defconfig 2>/dev/null
|
||
|
||
cd - > /dev/null
|
||
|
||
print_success "Packages configured"
|
||
return 0
|
||
}
|
||
|
||
# Build packages
|
||
build_packages() {
|
||
local single_package="$1"
|
||
|
||
print_header "Building Packages"
|
||
|
||
cd "$SDK_DIR"
|
||
|
||
# Detect package format based on OpenWrt version
|
||
local pkg_ext
|
||
if [[ "$OPENWRT_VERSION" =~ ^25\. ]] || [[ "$OPENWRT_VERSION" == "SNAPSHOT" ]]; then
|
||
pkg_ext="apk"
|
||
print_info "Building for OpenWrt $OPENWRT_VERSION (apk format)"
|
||
else
|
||
pkg_ext="ipk"
|
||
print_info "Building for OpenWrt $OPENWRT_VERSION (ipk format)"
|
||
fi
|
||
|
||
# Export for later use
|
||
export PKG_EXT="$pkg_ext"
|
||
|
||
local built=0
|
||
local failed=0
|
||
local built_list=""
|
||
local failed_list=""
|
||
|
||
# Determine which packages to build
|
||
local packages_to_build=()
|
||
if [[ -n "$single_package" ]]; then
|
||
if [[ -d "feeds/secubox/$single_package" ]]; then
|
||
packages_to_build=("$single_package")
|
||
else
|
||
print_error "Package $single_package not found in feed"
|
||
cd - > /dev/null
|
||
return 1
|
||
fi
|
||
else
|
||
# Build luci-app-* packages
|
||
for pkg in feeds/secubox/luci-app-*/; do
|
||
[[ -d "$pkg" ]] && packages_to_build+=("$(basename "$pkg")")
|
||
done
|
||
|
||
# Build luci-theme-* packages
|
||
for pkg in feeds/secubox/luci-theme-*/; do
|
||
[[ -d "$pkg" ]] && packages_to_build+=("$(basename "$pkg")")
|
||
done
|
||
|
||
# Build core secubox packages (secubox-app, nodogsplash, netifyd, etc.)
|
||
for pkg in feeds/secubox/secubox-*/; do
|
||
if [[ -d "$pkg" ]]; then
|
||
local pkg_name=$(basename "$pkg")
|
||
if is_openwrt_only_pkg "$pkg_name"; then
|
||
print_info "Skipping $pkg_name (requires OpenWrt buildroot)"
|
||
continue
|
||
fi
|
||
packages_to_build+=("$pkg_name")
|
||
fi
|
||
done
|
||
for pkg in feeds/secubox/nodogsplash/; do
|
||
if [[ -d "$pkg" ]]; then
|
||
local pkg_name=$(basename "$pkg")
|
||
if is_openwrt_only_pkg "$pkg_name"; then
|
||
print_info "Skipping $pkg_name (requires OpenWrt buildroot)"
|
||
continue
|
||
fi
|
||
packages_to_build+=("$pkg_name")
|
||
fi
|
||
done
|
||
for pkg in feeds/secubox/netifyd/; do
|
||
if [[ -d "$pkg" ]]; then
|
||
local pkg_name=$(basename "$pkg")
|
||
if is_openwrt_only_pkg "$pkg_name"; then
|
||
print_info "Skipping $pkg_name (requires OpenWrt buildroot)"
|
||
continue
|
||
fi
|
||
packages_to_build+=("$pkg_name")
|
||
fi
|
||
done
|
||
fi
|
||
|
||
# Build packages
|
||
for pkg_name in "${packages_to_build[@]}"; do
|
||
echo ""
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
echo "📦 Building: $pkg_name"
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
|
||
# Show package contents for debugging
|
||
echo "📁 Package contents:"
|
||
ls -la "feeds/secubox/$pkg_name"
|
||
|
||
# Build with timeout (10 minutes per package)
|
||
local build_log="/tmp/build-${pkg_name}.log"
|
||
|
||
# Build from feed (skip dependency checks for architecture-independent packages)
|
||
# These packages are just JavaScript/shell scripts - no compilation needed
|
||
if timeout 600 make "package/feeds/secubox/${pkg_name}/compile" V=s -j1 NO_DEPS=1 > "$build_log" 2>&1; then
|
||
# Check if package was created (.apk or .ipk)
|
||
local pkg_file=$(find bin -name "${pkg_name}*.${pkg_ext}" 2>/dev/null | head -1)
|
||
|
||
if [[ -n "$pkg_file" ]]; then
|
||
print_success "Built: $pkg_name"
|
||
echo " → $pkg_file"
|
||
built=$((built + 1))
|
||
built_list="${built_list}${pkg_name},"
|
||
else
|
||
print_warning "No .${pkg_ext} generated for $pkg_name"
|
||
echo "📋 Last 50 lines of build log:"
|
||
tail -50 "$build_log"
|
||
failed=$((failed + 1))
|
||
failed_list="${failed_list}${pkg_name},"
|
||
fi
|
||
else
|
||
print_error "Build failed: $pkg_name"
|
||
echo "📋 Last 100 lines of build log:"
|
||
tail -100 "$build_log"
|
||
failed=$((failed + 1))
|
||
failed_list="${failed_list}${pkg_name},"
|
||
fi
|
||
|
||
echo ""
|
||
done
|
||
|
||
cd - > /dev/null
|
||
|
||
echo ""
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
echo "📊 Build Summary"
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
print_success "Built: $built packages"
|
||
if [[ $failed -gt 0 ]]; then
|
||
print_error "Failed: $failed packages"
|
||
fi
|
||
echo ""
|
||
echo "Built: $built_list"
|
||
if [[ -n "$failed_list" ]]; then
|
||
echo "Failed: $failed_list"
|
||
fi
|
||
|
||
return 0
|
||
}
|
||
|
||
# Collect artifacts
|
||
collect_artifacts() {
|
||
print_header "Collecting Artifacts"
|
||
|
||
mkdir -p "$BUILD_DIR/$ARCH"
|
||
|
||
# Use package extension from build step
|
||
local pkg_ext="${PKG_EXT:-ipk}"
|
||
print_info "Package format: .${pkg_ext}"
|
||
|
||
# Find and copy package files (.apk or .ipk)
|
||
find "$SDK_DIR/bin" -name "luci-app-*.${pkg_ext}" -exec cp {} "$BUILD_DIR/$ARCH/" \; 2>/dev/null || true
|
||
find "$SDK_DIR/bin" -name "luci-theme-*.${pkg_ext}" -exec cp {} "$BUILD_DIR/$ARCH/" \; 2>/dev/null || true
|
||
|
||
# Also collect any SecuBox related packages
|
||
find "$SDK_DIR/bin" -name "*secubox*.${pkg_ext}" -exec cp {} "$BUILD_DIR/$ARCH/" \; 2>/dev/null || true
|
||
find "$SDK_DIR/bin" -name "netifyd*.${pkg_ext}" -exec cp {} "$BUILD_DIR/$ARCH/" \; 2>/dev/null || true
|
||
|
||
# Count
|
||
local pkg_count=$(find "$BUILD_DIR/$ARCH" -name "*.${pkg_ext}" 2>/dev/null | wc -l)
|
||
|
||
echo ""
|
||
print_info "Built packages for $ARCH:"
|
||
ls -la "$BUILD_DIR/$ARCH/" 2>/dev/null || echo "No packages"
|
||
|
||
# Create checksums
|
||
if [[ $pkg_count -gt 0 ]]; then
|
||
cd "$BUILD_DIR/$ARCH"
|
||
sha256sum ./*.${pkg_ext} > SHA256SUMS
|
||
cd - > /dev/null
|
||
fi
|
||
|
||
echo ""
|
||
print_success "Total: $pkg_count packages"
|
||
|
||
return 0
|
||
}
|
||
|
||
# Run validation
|
||
run_validation() {
|
||
print_header "Running Validation"
|
||
|
||
local failed=0
|
||
|
||
validate_makefiles || failed=$((failed + 1))
|
||
validate_json || failed=$((failed + 1))
|
||
validate_javascript || failed=$((failed + 1))
|
||
validate_shellscripts || failed=$((failed + 1))
|
||
check_file_permissions || failed=$((failed + 1))
|
||
|
||
if [[ $failed -gt 0 ]]; then
|
||
print_error "Validation failed with $failed error(s)"
|
||
return 1
|
||
fi
|
||
|
||
print_success "All validations passed!"
|
||
return 0
|
||
}
|
||
|
||
# Run build using OpenWrt buildroot (for packages that need system libraries like netifyd)
|
||
run_build_openwrt() {
|
||
local single_package="$1"
|
||
|
||
print_header "Building $single_package with OpenWrt Buildroot"
|
||
print_info "This package requires system libraries not available in SDK"
|
||
echo ""
|
||
|
||
check_dependencies
|
||
download_openwrt_source || return 1
|
||
setup_openwrt_feeds || return 1
|
||
copy_secubox_to_openwrt || return 1
|
||
|
||
cd "$OPENWRT_DIR"
|
||
|
||
# Map shorthand names to actual directory names in package/secubox/
|
||
declare -A DIR_NAME_MAP=(
|
||
["nodogsplash"]="secubox-app-nodogsplash"
|
||
["ndpid"]="secubox-app-ndpid"
|
||
["netifyd"]="secubox-app-netifyd"
|
||
["crowdsec"]="secubox-app-crowdsec"
|
||
["mitmproxy"]="secubox-app-mitmproxy"
|
||
)
|
||
|
||
# Map directory names to actual package names (PKG_NAME in Makefile)
|
||
# Only needed when directory name differs from PKG_NAME
|
||
declare -A PKG_NAME_MAP=(
|
||
["secubox-app-ndpid"]="ndpid"
|
||
["secubox-app-netifyd"]="secubox-netifyd"
|
||
["secubox-app-crowdsec"]="secubox-crowdsec"
|
||
["secubox-app-nodogsplash"]="secubox-app-nodogsplash"
|
||
["secubox-app-mitmproxy"]="secubox-app-mitmproxy"
|
||
)
|
||
|
||
# Resolve directory name (handle shorthand like "nodogsplash" -> "secubox-app-nodogsplash")
|
||
local dir_name="${DIR_NAME_MAP[$single_package]:-$single_package}"
|
||
|
||
# Get actual package name (for config and finding .ipk)
|
||
local pkg_name="${PKG_NAME_MAP[$dir_name]:-$dir_name}"
|
||
print_info "Input: $single_package -> Directory: $dir_name -> Package: $pkg_name"
|
||
|
||
# Update feeds
|
||
print_header "Installing Package from Feeds"
|
||
./scripts/feeds update -a
|
||
|
||
# For netifyd, remove old version from packages feed first
|
||
if [[ "$single_package" == "netifyd" ]]; then
|
||
./scripts/feeds uninstall netifyd 2>/dev/null || true
|
||
fi
|
||
|
||
# For Go packages (crowdsec, etc.), install golang build infrastructure first
|
||
if [[ "$dir_name" =~ ^(crowdsec|secubox-app-crowdsec)$ ]] || \
|
||
grep -q "golang-package.mk" "../package/secubox/$dir_name/Makefile" 2>/dev/null; then
|
||
print_info "Installing Go language support for $dir_name..."
|
||
./scripts/feeds install -a golang
|
||
fi
|
||
|
||
./scripts/feeds install -p secubox "$dir_name"
|
||
|
||
# Configure build for target architecture (mochabin = mvebu/cortexa72)
|
||
print_header "Configuring Build"
|
||
|
||
# Set target configuration based on ARCH
|
||
case "$ARCH" in
|
||
aarch64_cortex-a72|aarch64-cortex-a72)
|
||
echo "CONFIG_TARGET_mvebu=y" >> .config
|
||
echo "CONFIG_TARGET_mvebu_cortexa72=y" >> .config
|
||
;;
|
||
aarch64_cortex-a53|aarch64-cortex-a53)
|
||
echo "CONFIG_TARGET_mvebu=y" >> .config
|
||
echo "CONFIG_TARGET_mvebu_cortexa53=y" >> .config
|
||
;;
|
||
x86-64|x86_64)
|
||
echo "CONFIG_TARGET_x86=y" >> .config
|
||
echo "CONFIG_TARGET_x86_64=y" >> .config
|
||
;;
|
||
esac
|
||
|
||
# Enable the package (use actual package name, not directory name)
|
||
echo "CONFIG_PACKAGE_${pkg_name}=m" >> .config
|
||
make defconfig
|
||
|
||
# Build dependencies first (for packages like netifyd that need system libraries)
|
||
print_header "Building Dependencies"
|
||
print_info "Downloading and building required system libraries..."
|
||
make package/libs/toolchain/compile V=s 2>&1 | grep -v "^make\[" || true
|
||
|
||
# Build package
|
||
print_header "Building Package: $dir_name ($pkg_name)"
|
||
print_info "This may take several minutes on first build..."
|
||
echo ""
|
||
|
||
# Build from SecuBox feed (package/secubox/...)
|
||
if make package/secubox/"$dir_name"/compile V=s; then
|
||
print_success "Package built successfully"
|
||
|
||
# Find and display built package (search by actual package name)
|
||
local pkg_file=$(find bin/packages bin/targets -name "${pkg_name}*.ipk" -o -name "${pkg_name}_*.ipk" 2>/dev/null | head -1)
|
||
if [[ -z "$pkg_file" ]]; then
|
||
# Try alternative search patterns
|
||
pkg_file=$(find bin -name "*${pkg_name}*.ipk" 2>/dev/null | head -1)
|
||
fi
|
||
|
||
if [[ -n "$pkg_file" ]]; then
|
||
echo ""
|
||
echo "📦 Built package:"
|
||
ls -lh "$pkg_file"
|
||
echo ""
|
||
|
||
# Copy to build directory
|
||
mkdir -p "$BUILD_DIR/$ARCH"
|
||
cp "$pkg_file" "$BUILD_DIR/$ARCH/"
|
||
|
||
print_success "Package copied to: $BUILD_DIR/$ARCH/"
|
||
else
|
||
print_warning "Package file not found in bin/, checking build directory..."
|
||
# The package might be in targets instead of packages
|
||
pkg_file=$(find bin/targets -name "${pkg_name}*.ipk" 2>/dev/null | head -1)
|
||
if [[ -n "$pkg_file" ]]; then
|
||
echo "📦 Built package:"
|
||
ls -lh "$pkg_file"
|
||
mkdir -p "$BUILD_DIR/$ARCH"
|
||
cp "$pkg_file" "$BUILD_DIR/$ARCH/"
|
||
print_success "Package copied to: $BUILD_DIR/$ARCH/"
|
||
fi
|
||
fi
|
||
else
|
||
print_error "Package build failed"
|
||
return 1
|
||
fi
|
||
|
||
cd - > /dev/null
|
||
print_info "Syncing OpenWrt packages into firmware tree..."
|
||
ARCH_NAME="$ARCH_NAME" "$REPO_ROOT/secubox-tools/sync-openwrt-packages.sh" || print_warning "Package sync script failed"
|
||
return 0
|
||
}
|
||
|
||
# Run build
|
||
run_build() {
|
||
local single_package="$1"
|
||
|
||
# Packages that are OpenWrt buildroot only
|
||
if [[ -n "$single_package" ]] && is_openwrt_only_pkg "$single_package"; then
|
||
run_build_openwrt "$single_package"
|
||
return $?
|
||
fi
|
||
|
||
check_dependencies
|
||
download_sdk || return 1
|
||
setup_sdk_feeds || return 1
|
||
copy_packages "$single_package" || return 1
|
||
configure_packages "$single_package" || return 1
|
||
build_packages "$single_package" || return 1
|
||
collect_artifacts || return 1
|
||
|
||
print_header "Build Complete!"
|
||
print_success "Packages available in: $BUILD_DIR/$ARCH/"
|
||
|
||
return 0
|
||
}
|
||
|
||
# ============================================
|
||
# Firmware Image Building Functions
|
||
# ============================================
|
||
|
||
# Parse device profile
|
||
parse_device_profile() {
|
||
local device="$1"
|
||
|
||
if [[ -z "${DEVICE_PROFILES[$device]}" ]]; then
|
||
print_error "Unknown device: $device"
|
||
print_info "Available devices: ${!DEVICE_PROFILES[*]}"
|
||
return 1
|
||
fi
|
||
|
||
local profile="${DEVICE_PROFILES[$device]}"
|
||
IFS=':' read -r TARGET SUBTARGET PROFILE_NAME DESCRIPTION <<< "$profile"
|
||
|
||
export FW_TARGET="$TARGET"
|
||
export FW_SUBTARGET="$SUBTARGET"
|
||
export FW_PROFILE="$PROFILE_NAME"
|
||
export FW_DESCRIPTION="$DESCRIPTION"
|
||
export FW_DEVICE="$device"
|
||
|
||
return 0
|
||
}
|
||
|
||
# Download OpenWrt source
|
||
download_openwrt_source() {
|
||
print_header "Downloading OpenWrt Source"
|
||
|
||
if [[ -d "$OPENWRT_DIR/.git" ]]; then
|
||
print_info "OpenWrt source already exists, checking version..."
|
||
cd "$OPENWRT_DIR"
|
||
local current_version=$(git describe --tags 2>/dev/null || echo "unknown")
|
||
if [[ "$current_version" == "v${OPENWRT_VERSION}" ]]; then
|
||
print_success "Using existing OpenWrt $OPENWRT_VERSION"
|
||
cd - > /dev/null
|
||
return 0
|
||
else
|
||
print_info "Version mismatch (current: $current_version), re-cloning..."
|
||
cd - > /dev/null
|
||
rm -rf "$OPENWRT_DIR"
|
||
fi
|
||
fi
|
||
|
||
print_info "Cloning OpenWrt $OPENWRT_VERSION..."
|
||
|
||
if [[ "$OPENWRT_VERSION" == "SNAPSHOT" ]]; then
|
||
git clone --depth 1 https://github.com/openwrt/openwrt.git "$OPENWRT_DIR"
|
||
else
|
||
git clone --depth 1 --branch "v${OPENWRT_VERSION}" \
|
||
https://github.com/openwrt/openwrt.git "$OPENWRT_DIR"
|
||
fi
|
||
|
||
print_success "OpenWrt source downloaded"
|
||
return 0
|
||
}
|
||
|
||
# Setup OpenWrt feeds for firmware build
|
||
setup_openwrt_feeds() {
|
||
print_header "Setting up OpenWrt Feeds"
|
||
|
||
cd "$OPENWRT_DIR"
|
||
|
||
# Remove unwanted feeds
|
||
if [[ -f "feeds.conf.default" ]]; then
|
||
sed -i '/telephony/d' feeds.conf.default
|
||
sed -i '/routing/d' feeds.conf.default
|
||
print_success "Removed telephony and routing from feeds.conf.default"
|
||
fi
|
||
|
||
# Update feeds
|
||
print_info "Updating feeds (this may take a few minutes)..."
|
||
if ! ./scripts/feeds update -a 2>&1 | tee feed-update.log; then
|
||
print_warning "Feed update had errors, continuing..."
|
||
fi
|
||
|
||
# Install feeds
|
||
print_info "Installing feeds..."
|
||
if ! ./scripts/feeds install -a 2>&1 | tee feed-install.log; then
|
||
print_warning "Feed install had warnings, checking directories..."
|
||
fi
|
||
|
||
# Note: Skipping Lua header installation
|
||
# Our packages are PKGARCH:=all (scripts only) - no compilation needed
|
||
|
||
# Verify feeds
|
||
for feed in packages luci; do
|
||
if [[ -d "feeds/$feed" ]]; then
|
||
local feed_size=$(du -sh "feeds/$feed" 2>/dev/null | cut -f1)
|
||
print_success "feeds/$feed ($feed_size)"
|
||
else
|
||
print_error "feeds/$feed missing!"
|
||
cd - > /dev/null
|
||
return 1
|
||
fi
|
||
done
|
||
|
||
cd - > /dev/null
|
||
print_success "OpenWrt feeds configured"
|
||
return 0
|
||
}
|
||
|
||
# Copy SecuBox packages to OpenWrt
|
||
copy_secubox_to_openwrt() {
|
||
print_header "Copying SecuBox Packages to OpenWrt"
|
||
|
||
cd "$OPENWRT_DIR"
|
||
|
||
mkdir -p package/secubox
|
||
|
||
local pkg_count=0
|
||
|
||
# Copy luci-app-* packages
|
||
for pkg in ../../luci-app-*/; do
|
||
if [[ -d "$pkg" ]]; then
|
||
local pkg_name=$(basename "$pkg")
|
||
echo " ✅ $pkg_name"
|
||
cp -r "$pkg" package/secubox/
|
||
|
||
# Fix Makefile include path
|
||
if [[ -f "package/secubox/$pkg_name/Makefile" ]]; then
|
||
sed -i 's|include.*luci\.mk|include $(TOPDIR)/feeds/luci/luci.mk|' \
|
||
"package/secubox/$pkg_name/Makefile"
|
||
fi
|
||
|
||
pkg_count=$((pkg_count + 1))
|
||
fi
|
||
done
|
||
|
||
# Copy luci-theme-* packages
|
||
for pkg in ../../luci-theme-*/; do
|
||
if [[ -d "$pkg" ]]; then
|
||
local pkg_name=$(basename "$pkg")
|
||
echo " ✅ $pkg_name"
|
||
cp -r "$pkg" package/secubox/
|
||
|
||
# Fix Makefile include path
|
||
if [[ -f "package/secubox/$pkg_name/Makefile" ]]; then
|
||
sed -i 's|include.*luci\.mk|include $(TOPDIR)/feeds/luci/luci.mk|' \
|
||
"package/secubox/$pkg_name/Makefile"
|
||
fi
|
||
|
||
pkg_count=$((pkg_count + 1))
|
||
fi
|
||
done
|
||
|
||
# Copy secubox-app-* helper packages
|
||
for pkg in ../../package/secubox/secubox-app-*/; do
|
||
if [[ -d "$pkg" ]]; then
|
||
local pkg_name=$(basename "$pkg")
|
||
echo " ✅ $pkg_name"
|
||
cp -r "$pkg" package/secubox/
|
||
pkg_count=$((pkg_count + 1))
|
||
fi
|
||
done
|
||
|
||
# Copy additional core packages (non-LuCI / non-app store)
|
||
for pkg in ../../package/secubox/*/; do
|
||
if [[ -d "$pkg" ]]; then
|
||
local pkg_name=$(basename "$pkg")
|
||
echo " ✅ $pkg_name"
|
||
cp -r "$pkg" package/secubox/
|
||
pkg_count=$((pkg_count + 1))
|
||
fi
|
||
done
|
||
|
||
rm -f package/secubox/luci-app-secubox-netifyd/root/etc/config/netifyd >/dev/null 2>&1 || true
|
||
|
||
cd - > /dev/null
|
||
|
||
print_success "Copied $pkg_count SecuBox packages"
|
||
return 0
|
||
}
|
||
|
||
# Generate firmware configuration
|
||
generate_firmware_config() {
|
||
print_header "Generating Firmware Configuration"
|
||
|
||
cd "$OPENWRT_DIR"
|
||
|
||
print_info "Device: $FW_DESCRIPTION"
|
||
print_info "Target: $FW_TARGET/$FW_SUBTARGET"
|
||
print_info "Profile: $FW_PROFILE"
|
||
|
||
# Base configuration
|
||
cat > .config << EOF
|
||
# Target
|
||
CONFIG_TARGET_${FW_TARGET}=y
|
||
CONFIG_TARGET_${FW_TARGET}_${FW_SUBTARGET}=y
|
||
CONFIG_TARGET_${FW_TARGET}_${FW_SUBTARGET}_DEVICE_${FW_PROFILE}=y
|
||
|
||
# Image building (REQUIRED for firmware generation)
|
||
CONFIG_TARGET_MULTI_PROFILE=n
|
||
CONFIG_TARGET_ALL_PROFILES=n
|
||
CONFIG_TARGET_PER_DEVICE_ROOTFS=y
|
||
|
||
# Image settings
|
||
CONFIG_TARGET_ROOTFS_SQUASHFS=y
|
||
CONFIG_TARGET_ROOTFS_EXT4FS=y
|
||
CONFIG_TARGET_KERNEL_PARTSIZE=32
|
||
CONFIG_TARGET_ROOTFS_PARTSIZE=512
|
||
|
||
# Disable GDB in toolchain (fixes build issues)
|
||
# CONFIG_GDB is not set
|
||
CONFIG_BUILD_LOG=y
|
||
|
||
# Package conflict resolution
|
||
# CONFIG_PACKAGE_lucihttp is not set (fails in SDK)
|
||
# CONFIG_PACKAGE_cgi-io is not set (fails in SDK)
|
||
CONFIG_AUTOREMOVE=y
|
||
|
||
# Base packages
|
||
CONFIG_PACKAGE_luci=y
|
||
CONFIG_PACKAGE_luci-ssl=y
|
||
CONFIG_PACKAGE_luci-app-opkg=y
|
||
CONFIG_PACKAGE_luci-theme-openwrt-2020=y
|
||
CONFIG_PACKAGE_luci-theme-secubox=y
|
||
|
||
# DNS Server (fix conflict: use dnsmasq-full only)
|
||
# CONFIG_PACKAGE_dnsmasq is not set
|
||
CONFIG_PACKAGE_dnsmasq-full=y
|
||
|
||
# Networking essentials
|
||
CONFIG_PACKAGE_curl=y
|
||
CONFIG_PACKAGE_wget-ssl=y
|
||
CONFIG_PACKAGE_iptables=y
|
||
CONFIG_PACKAGE_ip6tables=y
|
||
|
||
# USB support
|
||
CONFIG_PACKAGE_kmod-usb-core=y
|
||
CONFIG_PACKAGE_kmod-usb3=y
|
||
CONFIG_PACKAGE_kmod-usb-storage=y
|
||
|
||
# Filesystem
|
||
CONFIG_PACKAGE_kmod-fs-ext4=y
|
||
CONFIG_PACKAGE_kmod-fs-vfat=y
|
||
|
||
# SecuBox packages - Core
|
||
CONFIG_PACKAGE_secubox-app=y
|
||
CONFIG_PACKAGE_luci-app-secubox=y
|
||
CONFIG_PACKAGE_luci-app-system-hub=y
|
||
CONFIG_PACKAGE_luci-app-secubox-admin=y
|
||
|
||
# SecuBox packages - Security & Monitoring
|
||
# CONFIG_PACKAGE_luci-app-crowdsec-dashboard is not set (requires crowdsec backend - compile fails)
|
||
CONFIG_PACKAGE_luci-app-netdata-dashboard=y
|
||
CONFIG_PACKAGE_crowdsec=y
|
||
CONFIG_PACKAGE_secubox-app-crowdsec=y
|
||
|
||
# SecuBox packages - Network Intelligence
|
||
CONFIG_PACKAGE_netifyd=y
|
||
CONFIG_PACKAGE_luci-app-secubox-netifyd=y
|
||
CONFIG_PACKAGE_luci-app-network-modes=y
|
||
|
||
# SecuBox packages - VPN & Access Control
|
||
CONFIG_PACKAGE_luci-app-wireguard-dashboard=y
|
||
CONFIG_PACKAGE_luci-app-client-guardian=y
|
||
# CONFIG_PACKAGE_luci-app-auth-guardian is not set (not stable yet)
|
||
|
||
# SecuBox packages - Bandwidth & Traffic
|
||
CONFIG_PACKAGE_luci-app-bandwidth-manager=y
|
||
CONFIG_PACKAGE_luci-app-media-flow=y
|
||
|
||
# SecuBox packages - Performance & Services
|
||
CONFIG_PACKAGE_luci-app-cdn-cache=y
|
||
CONFIG_PACKAGE_luci-app-vhost-manager=y
|
||
|
||
# SecuBox packages - Disabled (require compilation/not ready)
|
||
# CONFIG_PACKAGE_luci-app-ksm-manager is not set (not stable)
|
||
# CONFIG_PACKAGE_luci-app-traffic-shaper is not set (not stable)
|
||
|
||
# WireGuard
|
||
CONFIG_PACKAGE_wireguard-tools=y
|
||
CONFIG_PACKAGE_kmod-wireguard=y
|
||
CONFIG_PACKAGE_qrencode=y
|
||
EOF
|
||
|
||
# Device-specific packages
|
||
case "$FW_DEVICE" in
|
||
mochabin)
|
||
cat >> .config << EOF
|
||
|
||
# MOCHAbin specific - 10G networking
|
||
CONFIG_PACKAGE_kmod-sfp=y
|
||
CONFIG_PACKAGE_kmod-phy-marvell-10g=y
|
||
EOF
|
||
;;
|
||
espressobin-ultra)
|
||
cat >> .config << EOF
|
||
|
||
# WiFi support
|
||
CONFIG_PACKAGE_kmod-mt76=y
|
||
CONFIG_PACKAGE_kmod-mac80211=y
|
||
EOF
|
||
;;
|
||
esac
|
||
|
||
# Run defconfig
|
||
make defconfig 2>/dev/null
|
||
|
||
cd - > /dev/null
|
||
|
||
print_success "Configuration generated"
|
||
return 0
|
||
}
|
||
|
||
# Verify firmware configuration
|
||
verify_firmware_config() {
|
||
print_header "Verifying Firmware Configuration"
|
||
|
||
cd "$OPENWRT_DIR"
|
||
|
||
# Check device profile
|
||
if grep -q "CONFIG_TARGET_${FW_TARGET}_${FW_SUBTARGET}_DEVICE_${FW_PROFILE}=y" .config; then
|
||
print_success "Device profile correctly configured"
|
||
else
|
||
print_error "Device profile not found in .config!"
|
||
print_info "Searching for available profiles..."
|
||
find "target/$FW_TARGET/$FW_SUBTARGET" -name "*.mk" -exec grep -l "DEVICE_NAME" {} \; 2>/dev/null | head -5
|
||
cd - > /dev/null
|
||
return 1
|
||
fi
|
||
|
||
# Check image generation
|
||
if grep -q "CONFIG_TARGET_ROOTFS_SQUASHFS=y" .config; then
|
||
print_success "SQUASHFS image generation enabled"
|
||
fi
|
||
|
||
if grep -q "CONFIG_TARGET_ROOTFS_EXT4FS=y" .config; then
|
||
print_success "EXT4 image generation enabled"
|
||
fi
|
||
|
||
# Show relevant config
|
||
echo ""
|
||
print_info "Device configuration:"
|
||
grep "^CONFIG_TARGET_" .config | head -10
|
||
|
||
cd - > /dev/null
|
||
|
||
print_success "Configuration verified"
|
||
return 0
|
||
}
|
||
|
||
# Build firmware image
|
||
build_firmware_image() {
|
||
print_header "Building Firmware Image"
|
||
|
||
cd "$OPENWRT_DIR"
|
||
|
||
print_info "Device: $FW_DESCRIPTION"
|
||
print_info "Target: $FW_TARGET/$FW_SUBTARGET"
|
||
print_info "Profile: $FW_PROFILE"
|
||
print_info "CPU Cores: $(nproc)"
|
||
echo ""
|
||
|
||
local start_time=$(date +%s)
|
||
|
||
# Download packages first
|
||
print_info "Downloading packages..."
|
||
if ! make download -j$(nproc) V=s; then
|
||
print_warning "Parallel download failed, retrying single-threaded..."
|
||
make download -j1 V=s
|
||
fi
|
||
|
||
echo ""
|
||
print_header "Compiling Firmware (This may take 1-2 hours)"
|
||
echo ""
|
||
|
||
# Create necessary directories to avoid opkg lock file errors
|
||
# Find all root directories and ensure tmp subdirectories exist
|
||
find build_dir -type d -name "root.orig-*" -exec mkdir -p {}/tmp \; 2>/dev/null || true
|
||
find build_dir -type d -name "root-*" -exec mkdir -p {}/tmp \; 2>/dev/null || true
|
||
|
||
# Build with explicit PROFILE
|
||
if make -j$(nproc) PROFILE="$FW_PROFILE" V=s 2>&1 | tee build.log; then
|
||
local end_time=$(date +%s)
|
||
local duration=$((end_time - start_time))
|
||
local minutes=$((duration / 60))
|
||
local seconds=$((duration % 60))
|
||
|
||
print_success "Build completed in ${minutes}m ${seconds}s"
|
||
else
|
||
print_error "Parallel build failed, retrying single-threaded..."
|
||
|
||
# Ensure staging directories exist before retry
|
||
find build_dir -type d -name "root.orig-*" -exec mkdir -p {}/tmp \; 2>/dev/null || true
|
||
find build_dir -type d -name "root-*" -exec mkdir -p {}/tmp \; 2>/dev/null || true
|
||
|
||
if make -j1 PROFILE="$FW_PROFILE" V=s 2>&1 | tee build-retry.log; then
|
||
local end_time=$(date +%s)
|
||
local duration=$((end_time - start_time))
|
||
local minutes=$((duration / 60))
|
||
local seconds=$((duration % 60))
|
||
|
||
print_success "Build completed in ${minutes}m ${seconds}s (after retry)"
|
||
else
|
||
print_error "Build failed!"
|
||
echo ""
|
||
echo "Last 50 lines of build log:"
|
||
tail -50 build-retry.log
|
||
cd - > /dev/null
|
||
return 1
|
||
fi
|
||
fi
|
||
|
||
cd - > /dev/null
|
||
return 0
|
||
}
|
||
|
||
# Collect firmware artifacts
|
||
collect_firmware_artifacts() {
|
||
print_header "Collecting Firmware Artifacts"
|
||
|
||
local target_dir="$OPENWRT_DIR/bin/targets/$FW_TARGET/$FW_SUBTARGET"
|
||
local output_dir="$BUILD_DIR/firmware/$FW_DEVICE"
|
||
|
||
mkdir -p "$output_dir"
|
||
|
||
# Find and copy firmware images
|
||
local img_count=0
|
||
if [[ -d "$target_dir" ]]; then
|
||
echo "🔍 Target directory: $target_dir"
|
||
echo ""
|
||
echo "📂 All files in target directory:"
|
||
ls -lh "$target_dir" 2>/dev/null | grep -v "^total" || echo " (empty)"
|
||
echo ""
|
||
|
||
echo "📦 Copying firmware images..."
|
||
while IFS= read -r file; do
|
||
case "$(basename "$file")" in
|
||
*.ipk|*.manifest|*.json|sha256sums|*.buildinfo|packages)
|
||
continue
|
||
;;
|
||
*)
|
||
cp "$file" "$output_dir/"
|
||
print_success "$(basename "$file") ($(du -h "$file" | cut -f1))"
|
||
img_count=$((img_count + 1))
|
||
;;
|
||
esac
|
||
done < <(find "$target_dir" -maxdepth 1 -type f 2>/dev/null)
|
||
else
|
||
print_error "Target directory not found: $target_dir"
|
||
fi
|
||
|
||
if [[ $img_count -eq 0 ]]; then
|
||
echo ""
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
print_warning "No firmware images found!"
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
echo ""
|
||
|
||
# Diagnostic information
|
||
echo "🔍 Diagnostic Information:"
|
||
echo ""
|
||
|
||
if [[ -d "$OPENWRT_DIR/bin/targets" ]]; then
|
||
echo "📂 Available targets:"
|
||
find "$OPENWRT_DIR/bin/targets" -type d -mindepth 2 -maxdepth 2 2>/dev/null | sed 's|.*/bin/targets/||' || echo " (none)"
|
||
echo ""
|
||
fi
|
||
|
||
if [[ -f "$OPENWRT_DIR/build.log" ]]; then
|
||
echo "📋 Checking build log for errors..."
|
||
if grep -i "error\|failed\|cannot" "$OPENWRT_DIR/build.log" | tail -10 | grep -v "warning" > /tmp/fw-errors.txt 2>/dev/null && [[ -s /tmp/fw-errors.txt ]]; then
|
||
echo "Recent errors found:"
|
||
cat /tmp/fw-errors.txt
|
||
rm -f /tmp/fw-errors.txt
|
||
else
|
||
echo " No obvious errors in build log"
|
||
fi
|
||
echo ""
|
||
fi
|
||
|
||
if [[ -d "$target_dir" ]]; then
|
||
local all_files=$(find "$target_dir" -type f 2>/dev/null | wc -l)
|
||
echo "🎯 Target directory analysis:"
|
||
echo " Total files: $all_files"
|
||
if [[ $all_files -gt 0 ]]; then
|
||
echo " File types:"
|
||
find "$target_dir" -type f 2>/dev/null -exec basename {} \; | sed 's/.*\./ ./' | sort -u
|
||
fi
|
||
fi
|
||
|
||
echo ""
|
||
print_warning "This usually means:"
|
||
echo " 1. Device profile was not properly selected"
|
||
echo " 2. Build completed but only packages were built, not images"
|
||
echo " 3. Device profile name doesn't match OpenWrt $OPENWRT_VERSION"
|
||
echo ""
|
||
print_info "To debug:"
|
||
echo " 1. Check: $OPENWRT_DIR/.config for CONFIG_TARGET settings"
|
||
echo " 2. Review: $OPENWRT_DIR/build.log for errors"
|
||
echo " 3. Verify profile exists: find $OPENWRT_DIR/target/$FW_TARGET/$FW_SUBTARGET -name '*.mk'"
|
||
echo ""
|
||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||
fi
|
||
|
||
# Copy packages
|
||
mkdir -p "$output_dir/packages"
|
||
find "$OPENWRT_DIR/bin/packages" -name "luci-app-*.ipk" -exec cp {} "$output_dir/packages/" \; 2>/dev/null || true
|
||
find "$OPENWRT_DIR/bin/packages" -name "*secubox*.ipk" -exec cp {} "$output_dir/packages/" \; 2>/dev/null || true
|
||
find "$OPENWRT_DIR/bin/packages" -name "netifyd*.ipk" -exec cp {} "$output_dir/packages/" \; 2>/dev/null || true
|
||
|
||
local pkg_count=$(find "$output_dir/packages" -name "*.ipk" 2>/dev/null | wc -l)
|
||
|
||
# Generate checksums
|
||
cd "$output_dir"
|
||
sha256sum *.* > SHA256SUMS 2>/dev/null || true
|
||
if [[ -d packages && -n "$(ls -A packages 2>/dev/null)" ]]; then
|
||
(cd packages && sha256sum *.ipk > SHA256SUMS 2>/dev/null || true)
|
||
fi
|
||
cd - > /dev/null
|
||
|
||
# Create build info
|
||
cat > "$output_dir/BUILD_INFO.txt" << EOF
|
||
SecuBox Firmware Build
|
||
======================
|
||
Device: $FW_DESCRIPTION
|
||
Profile: $FW_PROFILE
|
||
Target: $FW_TARGET/$FW_SUBTARGET
|
||
OpenWrt: $OPENWRT_VERSION
|
||
Built: $(date -u +%Y-%m-%dT%H:%M:%SZ)
|
||
|
||
Firmware Images: $img_count
|
||
SecuBox Packages: $pkg_count
|
||
EOF
|
||
|
||
echo ""
|
||
print_success "Firmware images: $img_count"
|
||
print_success "SecuBox packages: $pkg_count"
|
||
print_success "Artifacts saved to: $output_dir"
|
||
|
||
echo ""
|
||
print_info "Contents:"
|
||
ls -lh "$output_dir"
|
||
|
||
return 0
|
||
}
|
||
|
||
# Debug firmware configuration
|
||
debug_firmware_build() {
|
||
local device="$1"
|
||
|
||
if [[ -z "$device" ]]; then
|
||
print_error "Device not specified"
|
||
print_info "Usage: $0 debug-firmware <device>"
|
||
print_info "Available devices: ${!DEVICE_PROFILES[*]}"
|
||
return 1
|
||
fi
|
||
|
||
# Parse device profile
|
||
parse_device_profile "$device" || return 1
|
||
|
||
print_header "Firmware Build Debug Information"
|
||
|
||
echo "Device Configuration:"
|
||
echo " Device: $FW_DEVICE"
|
||
echo " Description: $FW_DESCRIPTION"
|
||
echo " Target: $FW_TARGET"
|
||
echo " Subtarget: $FW_SUBTARGET"
|
||
echo " Profile: $FW_PROFILE"
|
||
echo ""
|
||
|
||
if [[ -d "$OPENWRT_DIR" ]]; then
|
||
print_info "OpenWrt source exists at: $OPENWRT_DIR"
|
||
|
||
if [[ -f "$OPENWRT_DIR/.config" ]]; then
|
||
echo ""
|
||
echo "Current .config settings:"
|
||
grep "^CONFIG_TARGET_" "$OPENWRT_DIR/.config" | head -20
|
||
|
||
echo ""
|
||
echo "Checking device profile..."
|
||
if grep -q "CONFIG_TARGET_${FW_TARGET}_${FW_SUBTARGET}_DEVICE_${FW_PROFILE}=y" "$OPENWRT_DIR/.config"; then
|
||
print_success "Device profile is configured"
|
||
else
|
||
print_error "Device profile NOT configured!"
|
||
fi
|
||
|
||
echo ""
|
||
echo "Available device profiles for $FW_TARGET/$FW_SUBTARGET:"
|
||
find "$OPENWRT_DIR/target/$FW_TARGET/$FW_SUBTARGET" -name "*.mk" 2>/dev/null | \
|
||
xargs grep -l "DEVICE_NAME" 2>/dev/null | head -10
|
||
else
|
||
print_warning "No .config file found - run build-firmware first"
|
||
fi
|
||
|
||
echo ""
|
||
if [[ -d "$OPENWRT_DIR/bin/targets/$FW_TARGET/$FW_SUBTARGET" ]]; then
|
||
echo "Build output directory exists:"
|
||
echo " Path: $OPENWRT_DIR/bin/targets/$FW_TARGET/$FW_SUBTARGET"
|
||
echo " Files:"
|
||
ls -lh "$OPENWRT_DIR/bin/targets/$FW_TARGET/$FW_SUBTARGET" 2>/dev/null | grep -v "^total" | head -20
|
||
else
|
||
print_warning "Build output directory doesn't exist yet"
|
||
fi
|
||
else
|
||
print_warning "OpenWrt source not downloaded yet"
|
||
print_info "Run: $0 build-firmware $device"
|
||
fi
|
||
|
||
return 0
|
||
}
|
||
|
||
# Run firmware build
|
||
run_firmware_build() {
|
||
local device="$1"
|
||
|
||
if [[ -z "$device" ]]; then
|
||
print_error "Device not specified"
|
||
print_info "Usage: $0 build-firmware <device>"
|
||
print_info "Available devices: ${!DEVICE_PROFILES[*]}"
|
||
return 1
|
||
fi
|
||
|
||
# Parse device profile
|
||
parse_device_profile "$device" || return 1
|
||
|
||
# Check dependencies
|
||
check_dependencies
|
||
|
||
# Build firmware
|
||
download_openwrt_source || return 1
|
||
setup_openwrt_feeds || return 1
|
||
copy_secubox_to_openwrt || return 1
|
||
generate_firmware_config || return 1
|
||
verify_firmware_config || return 1
|
||
build_firmware_image || return 1
|
||
collect_firmware_artifacts || return 1
|
||
|
||
print_header "Firmware Build Complete!"
|
||
print_success "Device: $FW_DESCRIPTION"
|
||
print_success "Location: $BUILD_DIR/firmware/$FW_DEVICE/"
|
||
|
||
return 0
|
||
}
|
||
|
||
# Show usage
|
||
show_usage() {
|
||
cat << EOF
|
||
SecuBox Local Build Tool
|
||
Replicates GitHub Actions workflows for local development
|
||
|
||
USAGE:
|
||
$0 <command> [options]
|
||
|
||
COMMANDS:
|
||
validate Run validation only (lint, syntax checks)
|
||
build Build all packages for x86_64
|
||
build <package> Build single package
|
||
build --arch <arch> Build for specific architecture
|
||
build-firmware <device> Build full firmware image for device
|
||
debug-firmware <device> Debug firmware build (check config without building)
|
||
full Run validation then build
|
||
clean Clean build directories
|
||
clean-all Clean all build directories including OpenWrt source
|
||
help Show this help message
|
||
|
||
PACKAGES:
|
||
SDK packages (scripts only, fast build):
|
||
luci-app-* LuCI application packages
|
||
luci-theme-* LuCI theme packages
|
||
|
||
Toolchain packages (native code, requires full OpenWrt build):
|
||
ndpid nDPId DPI engine (shorthand for secubox-app-ndpid)
|
||
netifyd Netifyd DPI engine (shorthand for secubox-app-netifyd)
|
||
nodogsplash Captive portal (shorthand for secubox-app-nodogsplash)
|
||
crowdsec CrowdSec IPS (shorthand for secubox-app-crowdsec)
|
||
mitmproxy mitmproxy HTTPS proxy (shorthand for secubox-app-mitmproxy)
|
||
secubox-app-* Full directory names also accepted
|
||
|
||
ARCHITECTURES (for package building):
|
||
aarch64-cortex-a72 ARM Cortex-A72 (MOCHAbin, RPi4) (default)
|
||
aarch64-cortex-a53 ARM Cortex-A53 (ESPRESSObin)
|
||
x86-64 PC, VMs
|
||
aarch64-generic Generic ARM64
|
||
mips-24kc MIPS 24Kc (TP-Link)
|
||
mipsel-24kc MIPS LE (Xiaomi, GL.iNet)
|
||
|
||
DEVICES (for firmware building):
|
||
espressobin-v7 ESPRESSObin V7 (1-2GB DDR4)
|
||
espressobin-ultra ESPRESSObin Ultra (PoE, WiFi)
|
||
mochabin MOCHAbin (Quad-core A72, 10G)
|
||
x86-64 x86_64 Generic PC
|
||
|
||
EXAMPLES:
|
||
# Validate all packages
|
||
$0 validate
|
||
|
||
# Build all SDK packages for default architecture (mochabin)
|
||
$0 build
|
||
|
||
# Build single LuCI package (SDK - fast)
|
||
$0 build luci-app-system-hub
|
||
|
||
# Build nDPId DPI engine (toolchain - native code)
|
||
$0 build ndpid
|
||
|
||
# Build Netifyd DPI engine (toolchain)
|
||
$0 build netifyd
|
||
|
||
# Build Nodogsplash captive portal (toolchain)
|
||
$0 build nodogsplash
|
||
|
||
# Build CrowdSec IPS (toolchain - Go)
|
||
$0 build crowdsec
|
||
|
||
# Build mitmproxy HTTPS proxy (toolchain - binary download)
|
||
$0 build mitmproxy
|
||
|
||
# Build using full directory name
|
||
$0 build secubox-app-ndpid
|
||
|
||
# Build netifyd LuCI app (SDK - scripts only)
|
||
$0 build luci-app-secubox-netifyd
|
||
|
||
# Build for specific architecture
|
||
$0 build ndpid --arch x86-64
|
||
|
||
# Build firmware image for MOCHAbin
|
||
$0 build-firmware mochabin
|
||
|
||
# Build firmware image for ESPRESSObin V7
|
||
$0 build-firmware espressobin-v7
|
||
|
||
# Debug firmware build configuration
|
||
$0 debug-firmware mochabin
|
||
|
||
# Full validation and build
|
||
$0 full
|
||
|
||
# Clean build artifacts
|
||
$0 clean
|
||
|
||
# Clean everything including OpenWrt source
|
||
$0 clean-all
|
||
|
||
ENVIRONMENT VARIABLES:
|
||
OPENWRT_VERSION OpenWrt version (default: 24.10.5)
|
||
SDK_DIR SDK directory (default: ./sdk)
|
||
BUILD_DIR Build output directory (default: ./build)
|
||
CACHE_DIR Download cache directory (default: ./cache)
|
||
OPENWRT_DIR OpenWrt source directory for firmware builds (default: ./openwrt)
|
||
|
||
EOF
|
||
}
|
||
|
||
# Main script
|
||
main() {
|
||
# Change to script directory
|
||
cd "$(dirname "$0")"
|
||
|
||
local command="${1:-help}"
|
||
shift || true
|
||
|
||
case "$command" in
|
||
validate)
|
||
run_validation
|
||
;;
|
||
|
||
build)
|
||
local single_package=""
|
||
local arch_specified=false
|
||
|
||
while [[ $# -gt 0 ]]; do
|
||
case "$1" in
|
||
--arch)
|
||
set_architecture "$2"
|
||
arch_specified=true
|
||
shift 2
|
||
;;
|
||
luci-app-*|luci-theme-*|secubox-app-*|secubox-*|netifyd|ndpid|nodogsplash|crowdsec|mitmproxy)
|
||
single_package="$1"
|
||
shift
|
||
;;
|
||
*)
|
||
print_error "Unknown option: $1"
|
||
show_usage
|
||
exit 1
|
||
;;
|
||
esac
|
||
done
|
||
|
||
run_build "$single_package"
|
||
;;
|
||
|
||
build-firmware)
|
||
local device="$1"
|
||
if [[ -z "$device" ]]; then
|
||
print_error "Device not specified"
|
||
print_info "Usage: $0 build-firmware <device>"
|
||
print_info "Available devices: ${!DEVICE_PROFILES[*]}"
|
||
exit 1
|
||
fi
|
||
run_firmware_build "$device"
|
||
;;
|
||
|
||
debug-firmware)
|
||
local device="$1"
|
||
if [[ -z "$device" ]]; then
|
||
print_error "Device not specified"
|
||
print_info "Usage: $0 debug-firmware <device>"
|
||
print_info "Available devices: ${!DEVICE_PROFILES[*]}"
|
||
exit 1
|
||
fi
|
||
debug_firmware_build "$device"
|
||
;;
|
||
|
||
full)
|
||
run_validation && run_build
|
||
;;
|
||
|
||
clean)
|
||
print_header "Cleaning Build Directories"
|
||
rm -rf "$SDK_DIR" "$BUILD_DIR"
|
||
print_success "Build directories cleaned"
|
||
;;
|
||
|
||
clean-all)
|
||
print_header "Cleaning All Build Directories"
|
||
rm -rf "$SDK_DIR" "$BUILD_DIR" "$OPENWRT_DIR" "$CACHE_DIR"
|
||
print_success "All build directories cleaned (SDK, build, OpenWrt source, cache)"
|
||
;;
|
||
|
||
help|--help|-h)
|
||
show_usage
|
||
;;
|
||
|
||
*)
|
||
print_error "Unknown command: $command"
|
||
echo ""
|
||
show_usage
|
||
exit 1
|
||
;;
|
||
esac
|
||
}
|
||
|
||
# Run main
|
||
main "$@"
|