name: Test & Validate Packages on: push: branches: [main, master, develop] pull_request: branches: [main, master] jobs: # ============================================ # Lint and validate package structure # ============================================ lint: runs-on: ubuntu-latest name: Lint & Validate steps: - name: Checkout uses: actions/checkout@v4 - name: Install validators run: | sudo apt-get update sudo apt-get install -y shellcheck jq - name: Validate Makefile structure run: | echo "📋 Validating Makefile structure..." ERRORS=0 for makefile in luci-app-*/Makefile; do if [[ -f "$makefile" ]]; then PKG=$(dirname "$makefile") echo " 🔍 Checking $PKG..." # Required fields REQUIRED_FIELDS=( "PKG_NAME" "PKG_VERSION" "PKG_RELEASE" "PKG_LICENSE" ) for field in "${REQUIRED_FIELDS[@]}"; do if ! grep -q "^${field}:=" "$makefile"; then echo " ❌ Missing: $field" ERRORS=$((ERRORS + 1)) fi done # Check for include statements if ! grep -q "include.*luci.mk\|include.*package.mk" "$makefile"; then echo " ❌ Missing include statement (luci.mk or package.mk)" ERRORS=$((ERRORS + 1)) fi fi done if [[ $ERRORS -gt 0 ]]; then echo "❌ Found $ERRORS errors" exit 1 fi echo "✅ All Makefiles valid" - name: Validate JSON files run: | echo "📋 Validating JSON files..." ERRORS=0 while IFS= read -r jsonfile; do echo " 🔍 Checking $jsonfile..." if ! jq empty "$jsonfile" 2>/dev/null; then echo " ❌ Invalid JSON" ERRORS=$((ERRORS + 1)) fi done < <(find . -name "*.json" -type f) if [[ $ERRORS -gt 0 ]]; then echo "❌ Found $ERRORS JSON errors" exit 1 fi echo "✅ All JSON files valid" - name: Validate JavaScript syntax run: | echo "📋 Validating JavaScript files..." curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - sudo apt-get install -y nodejs ERRORS=0 while IFS= read -r jsfile; do echo " 🔍 Checking $jsfile..." if ! node --check "$jsfile" 2>/dev/null; then echo " ❌ Syntax error" ERRORS=$((ERRORS + 1)) fi done < <(find . -name "*.js" -type f ! -path "*/node_modules/*") if [[ $ERRORS -gt 0 ]]; then echo "❌ Found $ERRORS JavaScript errors" exit 1 fi echo "✅ All JavaScript files valid" - name: Validate shell scripts run: | echo "📋 Validating shell scripts..." WARNINGS=0 # Check RPCD scripts while IFS= read -r script; do echo " 🔍 Checking $script..." shellcheck -s sh "$script" || WARNINGS=$((WARNINGS + 1)) done < <(find . -path "*/rpcd/*" -type f 2>/dev/null) # Check init scripts while IFS= read -r script; do echo " 🔍 Checking $script..." shellcheck -s sh "$script" || WARNINGS=$((WARNINGS + 1)) done < <(find . -path "*/init.d/*" -type f 2>/dev/null) if [[ $WARNINGS -gt 0 ]]; then echo "⚠️ Found $WARNINGS shellcheck warnings (non-blocking)" fi echo "✅ Shell script validation complete" - name: Check file permissions run: | echo "📋 Checking file permissions..." ERRORS=0 # RPCD scripts should be executable while IFS= read -r script; do if [[ ! -x "$script" ]]; then echo " ❌ Not executable: $script" chmod +x "$script" ERRORS=$((ERRORS + 1)) fi done < <(find . -path "*/usr/libexec/rpcd/*" -type f 2>/dev/null) # Init scripts should be executable while IFS= read -r script; do if [[ ! -x "$script" ]]; then echo " ❌ Not executable: $script" chmod +x "$script" ERRORS=$((ERRORS + 1)) fi done < <(find . -path "*/etc/init.d/*" -type f 2>/dev/null) if [[ $ERRORS -gt 0 ]]; then echo "⚠️ Fixed $ERRORS permission issues" fi echo "✅ File permissions checked" - name: Validate package structure run: | echo "📋 Validating package structure..." for pkg in luci-app-*/; do if [[ -d "$pkg" ]]; then echo " 📦 Checking $pkg..." # Required if [[ ! -f "${pkg}Makefile" ]]; then echo " ❌ Missing required: Makefile" exit 1 fi # Recommended RECOMMENDED=( "htdocs/luci-static/resources" "root/usr/share/luci/menu.d" "root/usr/share/rpcd/acl.d" ) for rec in "${RECOMMENDED[@]}"; do if [[ ! -e "${pkg}${rec}" ]]; then echo " ⚠️ Missing recommended: $rec" fi done fi done echo "✅ Package structure valid" # ============================================ # Quick build test on x86_64 # ============================================ test-build: runs-on: ubuntu-latest name: Test Build (x86_64) needs: lint steps: - name: Checkout uses: actions/checkout@v4 - name: Install dependencies run: | sudo apt-get update sudo apt-get install -y \ build-essential clang flex bison g++ gawk \ gcc-multilib g++-multilib gettext git libncurses5-dev \ libssl-dev python3-setuptools python3-dev rsync unzip zlib1g-dev wget - name: Cache OpenWrt SDK uses: actions/cache@v4 id: cache-sdk with: path: ~/sdk key: openwrt-sdk-23.05.5-x86-64-test-v2 - name: Download OpenWrt SDK if: steps.cache-sdk.outputs.cache-hit != 'true' run: | SDK_URL="https://downloads.openwrt.org/releases/23.05.5/targets/x86/64" SDK_FILE=$(curl -sL "$SDK_URL/" | grep -oP 'openwrt-sdk[^"]+\.tar\.xz' | head -1) wget -q "${SDK_URL}/${SDK_FILE}" -O /tmp/sdk.tar.xz mkdir -p ~/sdk tar -xf /tmp/sdk.tar.xz -C ~/sdk --strip-components=1 - name: Prepare SDK run: | cd ~/sdk ./scripts/feeds update -a ./scripts/feeds install -a make defconfig - name: Copy packages run: | # IMPORTANT: Copy packages DIRECTLY into package/, not into a subdirectory for pkg in luci-app-*/; do if [[ -d "$pkg" && -f "${pkg}Makefile" ]]; then PKG_NAME=$(basename "$pkg") echo "📦 Copying $PKG_NAME..." cp -r "$pkg" ~/sdk/package/ fi done echo "" echo "📋 Packages in SDK:" ls -d ~/sdk/package/luci-app-*/ 2>/dev/null || echo "No packages found" - name: Configure packages run: | cd ~/sdk # Enable packages for pkg in ~/sdk/package/luci-app-*/; do if [[ -d "$pkg" ]]; then PKG_NAME=$(basename "$pkg") echo "CONFIG_PACKAGE_${PKG_NAME}=m" >> .config echo "✅ Enabled: $PKG_NAME" fi done make defconfig - name: Build packages run: | cd ~/sdk echo "🔨 Building packages..." BUILT=0 FAILED=0 # Build each package individually with timeout for pkg in ~/sdk/package/luci-app-*/; do if [[ -d "$pkg" ]]; then PKG_NAME=$(basename "$pkg") echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "📦 Building: $PKG_NAME" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━" # Build with 5 minute timeout per package if timeout 5m make package/${PKG_NAME}/compile V=s -j$(nproc) 2>&1; then echo "✅ Built: $PKG_NAME" BUILT=$((BUILT + 1)) else echo "❌ Failed: $PKG_NAME" FAILED=$((FAILED + 1)) fi fi done echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "📊 Build Summary" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "Built: $BUILT packages" echo "Failed: $FAILED packages" if [[ $BUILT -eq 0 ]]; then echo "❌ No packages were built!" exit 1 fi - name: Verify output run: | echo "📋 Built packages:" find ~/sdk/bin -name "luci-app-*.ipk" -exec ls -la {} \; PKG_COUNT=$(find ~/sdk/bin -name "luci-app-*.ipk" | wc -l) echo "" echo "📦 Total packages built: $PKG_COUNT" if [[ $PKG_COUNT -eq 0 ]]; then echo "❌ No packages were built!" exit 1 fi echo "✅ Build test passed" # ============================================ # Generate documentation # ============================================ docs: runs-on: ubuntu-latest name: Generate Docs needs: lint steps: - name: Checkout uses: actions/checkout@v4 - name: Generate package list run: | echo "# SecuBox Packages" > PACKAGES.md echo "" >> PACKAGES.md echo "Auto-generated package documentation." >> PACKAGES.md echo "" >> PACKAGES.md echo "| Package | Version | Description |" >> PACKAGES.md echo "|---------|---------|-------------|" >> PACKAGES.md for makefile in luci-app-*/Makefile; do if [[ -f "$makefile" ]]; then PKG_NAME=$(grep "^PKG_NAME:=" "$makefile" | cut -d'=' -f2) PKG_VERSION=$(grep "^PKG_VERSION:=" "$makefile" | cut -d'=' -f2) PKG_TITLE=$(grep "^LUCI_TITLE:=" "$makefile" | cut -d'=' -f2- | sed 's/^[[:space:]]*//') # Fallback if LUCI_TITLE not found if [[ -z "$PKG_TITLE" ]]; then PKG_TITLE=$(grep "TITLE:=" "$makefile" | head -1 | cut -d'=' -f2- | sed 's/^[[:space:]]*//') fi echo "| $PKG_NAME | $PKG_VERSION | $PKG_TITLE |" >> PACKAGES.md fi done echo "" >> PACKAGES.md echo "---" >> PACKAGES.md echo "Generated: $(date -u +%Y-%m-%dT%H:%M:%SZ)" >> PACKAGES.md echo "📋 Generated PACKAGES.md:" cat PACKAGES.md - name: Upload docs uses: actions/upload-artifact@v4 with: name: documentation path: PACKAGES.md