From 0981987c10704c71d7b9658db4215d6558f15b13 Mon Sep 17 00:00:00 2001 From: CyberMind-FR Date: Sun, 21 Dec 2025 09:46:33 +0100 Subject: [PATCH] Initial commit: SecuBox v1.0.0 --- .github/workflows/build-openwrt-packages.yml | 551 +++++++++++++++++++ .github/workflows/build-secubox-images.yml | 398 ++++++++++++++ .github/workflows/test-validate.yml | 327 +++++++++++ README.md | 232 ++++++++ build-openwrt-packages.yml | 551 +++++++++++++++++++ build-secubox-images.yml | 398 ++++++++++++++ luci-app-client-guardian | 1 + luci-app-crowdsec-dashboard | 1 + luci-app-netdata-dashboard | 1 + luci-app-netifyd-dashboard | 1 + luci-app-network-modes | 1 + luci-app-system-hub | 1 + luci-app-wireguard-dashboard | 1 + templates/Makefile.template | 128 +++++ test-validate.yml | 327 +++++++++++ 15 files changed, 2919 insertions(+) create mode 100644 .github/workflows/build-openwrt-packages.yml create mode 100644 .github/workflows/build-secubox-images.yml create mode 100644 .github/workflows/test-validate.yml create mode 100644 README.md create mode 100644 build-openwrt-packages.yml create mode 100644 build-secubox-images.yml create mode 160000 luci-app-client-guardian create mode 160000 luci-app-crowdsec-dashboard create mode 160000 luci-app-netdata-dashboard create mode 160000 luci-app-netifyd-dashboard create mode 160000 luci-app-network-modes create mode 160000 luci-app-system-hub create mode 160000 luci-app-wireguard-dashboard create mode 100644 templates/Makefile.template create mode 100644 test-validate.yml diff --git a/.github/workflows/build-openwrt-packages.yml b/.github/workflows/build-openwrt-packages.yml new file mode 100644 index 00000000..caa2f1ad --- /dev/null +++ b/.github/workflows/build-openwrt-packages.yml @@ -0,0 +1,551 @@ +name: Build OpenWrt Packages + +on: + push: + branches: [main, master, develop] + tags: + - 'v*' + pull_request: + branches: [main, master] + workflow_dispatch: + inputs: + openwrt_version: + description: 'OpenWrt version' + required: true + default: '23.05.5' + type: choice + options: + - '23.05.5' + - '23.05.4' + - '22.03.7' + - 'SNAPSHOT' + architectures: + description: 'Architectures to build (comma-separated or "all")' + required: false + default: 'all' + +env: + OPENWRT_VERSION: ${{ github.event.inputs.openwrt_version || '23.05.5' }} + +jobs: + # ============================================ + # Determine build matrix + # ============================================ + setup: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + version: ${{ steps.version.outputs.version }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Determine version + id: version + run: | + if [[ "${{ github.ref }}" == refs/tags/v* ]]; then + VERSION="${{ github.ref_name }}" + else + VERSION="0.0.0-$(git rev-parse --short HEAD)" + fi + echo "version=${VERSION#v}" >> $GITHUB_OUTPUT + echo "πŸ“¦ Package version: ${VERSION#v}" + + - name: Set build matrix + id: set-matrix + run: | + # Full architecture matrix for OpenWrt + # Format: target/subtarget -> SDK name mapping + ALL_TARGETS=$(cat << 'EOF' + { + "include": [ + { + "target": "x86-64", + "arch": "x86_64", + "sdk_name": "x86-64", + "description": "x86 64-bit (PC, VM, containers)" + }, + { + "target": "x86-generic", + "arch": "i386_pentium4", + "sdk_name": "x86-generic", + "description": "x86 32-bit (legacy PC)" + }, + { + "target": "aarch64-generic", + "arch": "aarch64_generic", + "sdk_name": "armsr-armv8", + "description": "ARM 64-bit generic (RPi4, Rock64)" + }, + { + "target": "aarch64-cortex-a53", + "arch": "aarch64_cortex-a53", + "sdk_name": "mvebu-cortexa53", + "description": "ARM Cortex-A53 (ESPRESSObin, Sheeva64)" + }, + { + "target": "aarch64-cortex-a72", + "arch": "aarch64_cortex-a72", + "sdk_name": "mvebu-cortexa72", + "description": "ARM Cortex-A72 (MOCHAbin, MACCHIATObin)" + }, + { + "target": "arm-cortex-a7-neon", + "arch": "arm_cortex-a7_neon-vfpv4", + "sdk_name": "sunxi-cortexa7", + "description": "ARM Cortex-A7 (Orange Pi, Banana Pi)" + }, + { + "target": "arm-cortex-a9-neon", + "arch": "arm_cortex-a9_neon", + "sdk_name": "mvebu-cortexa9", + "description": "ARM Cortex-A9 (Linksys WRT, Turris)" + }, + { + "target": "arm-cortex-a15-neon", + "arch": "arm_cortex-a15_neon-vfpv4", + "sdk_name": "armvirt-32", + "description": "ARM Cortex-A15 (QEMU ARM)" + }, + { + "target": "mips-24kc", + "arch": "mips_24kc", + "sdk_name": "ath79-generic", + "description": "MIPS 24Kc (TP-Link, Ubiquiti)" + }, + { + "target": "mipsel-24kc", + "arch": "mipsel_24kc", + "sdk_name": "ramips-mt7621", + "description": "MIPS Little-Endian (Xiaomi, GL.iNet)" + }, + { + "target": "mipsel-74kc", + "arch": "mipsel_74kc", + "sdk_name": "bcm47xx-mips74k", + "description": "MIPS 74Kc (Broadcom routers)" + }, + { + "target": "mediatek-filogic", + "arch": "aarch64_cortex-a53", + "sdk_name": "mediatek-filogic", + "description": "MediaTek Filogic (MT7981, MT7986)" + }, + { + "target": "qualcomm-ipq40xx", + "arch": "arm_cortex-a7_neon-vfpv4", + "sdk_name": "ipq40xx-generic", + "description": "Qualcomm IPQ40xx (Google WiFi, Zyxel)" + }, + { + "target": "qualcomm-ipq806x", + "arch": "arm_cortex-a15_neon-vfpv4", + "sdk_name": "ipq806x-generic", + "description": "Qualcomm IPQ806x (Netgear R7800)" + }, + { + "target": "rockchip-armv8", + "arch": "aarch64_generic", + "sdk_name": "rockchip-armv8", + "description": "Rockchip (NanoPi R4S, R5S)" + }, + { + "target": "bcm27xx-bcm2711", + "arch": "aarch64_cortex-a72", + "sdk_name": "bcm27xx-bcm2711", + "description": "Raspberry Pi 4" + } + ] + } + EOF + ) + + INPUT_ARCHS="${{ github.event.inputs.architectures }}" + if [[ -z "$INPUT_ARCHS" || "$INPUT_ARCHS" == "all" ]]; then + MATRIX="$ALL_TARGETS" + else + # Filter matrix based on input + MATRIX=$(echo "$ALL_TARGETS" | jq -c --arg archs "$INPUT_ARCHS" ' + .include |= map(select(.target as $t | $archs | split(",") | map(gsub("^\\s+|\\s+$";"")) | any(. == $t or . == "all"))) + ') + fi + + echo "matrix=$MATRIX" >> $GITHUB_OUTPUT + echo "πŸ“‹ Build matrix:" + echo "$MATRIX" | jq '.' + + # ============================================ + # Build packages for each architecture + # ============================================ + build: + needs: setup + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.setup.outputs.matrix) }} + + name: Build ${{ matrix.target }} + + steps: + - name: Checkout source + uses: actions/checkout@v4 + + - name: Free disk space + run: | + echo "🧹 Cleaning up disk space..." + sudo rm -rf /usr/share/dotnet + sudo rm -rf /usr/local/lib/android + sudo rm -rf /opt/ghc + sudo rm -rf /opt/hostedtoolcache/CodeQL + sudo docker image prune --all --force + df -h + + - 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-distutils python3-setuptools rsync \ + swig unzip zlib1g-dev file wget curl jq + + - name: Cache OpenWrt SDK + uses: actions/cache@v4 + id: cache-sdk + with: + path: ~/sdk + key: openwrt-sdk-${{ env.OPENWRT_VERSION }}-${{ matrix.sdk_name }} + + - name: Download OpenWrt SDK + if: steps.cache-sdk.outputs.cache-hit != 'true' + run: | + echo "πŸ“₯ Downloading SDK for ${{ matrix.description }}..." + + VERSION="${{ env.OPENWRT_VERSION }}" + SDK_NAME="${{ matrix.sdk_name }}" + + if [[ "$VERSION" == "SNAPSHOT" ]]; then + BASE_URL="https://downloads.openwrt.org/snapshots/targets" + else + BASE_URL="https://downloads.openwrt.org/releases/${VERSION}/targets" + fi + + # Parse target/subtarget from sdk_name + TARGET=$(echo "$SDK_NAME" | cut -d'-' -f1) + SUBTARGET=$(echo "$SDK_NAME" | cut -d'-' -f2-) + + SDK_URL="${BASE_URL}/${TARGET}/${SUBTARGET}" + + echo "πŸ” Looking for SDK at: $SDK_URL" + + # Find SDK filename + SDK_FILE=$(curl -sL "$SDK_URL/" | grep -oP 'openwrt-sdk[^"]+\.tar\.(xz|zst)' | head -1) + + if [[ -z "$SDK_FILE" ]]; then + echo "⚠️ SDK not found, trying alternative URL pattern..." + SDK_FILE=$(curl -sL "$SDK_URL/sha256sums" | grep -oP 'openwrt-sdk[^\s]+' | head -1) + fi + + if [[ -z "$SDK_FILE" ]]; then + echo "❌ Could not find SDK for ${{ matrix.target }}" + echo "πŸ”— Checked: $SDK_URL" + exit 1 + fi + + echo "πŸ“¦ Downloading: $SDK_FILE" + wget -q --show-progress "${SDK_URL}/${SDK_FILE}" -O /tmp/sdk.tar.xz || \ + wget -q --show-progress "${SDK_URL}/${SDK_FILE}" -O /tmp/sdk.tar.zst + + mkdir -p ~/sdk + if [[ "$SDK_FILE" == *.zst ]]; then + zstd -d /tmp/sdk.tar.zst -o /tmp/sdk.tar + tar -xf /tmp/sdk.tar -C ~/sdk --strip-components=1 + else + tar -xf /tmp/sdk.tar.xz -C ~/sdk --strip-components=1 + fi + + echo "βœ… SDK extracted to ~/sdk" + + - name: Prepare SDK + run: | + cd ~/sdk + + # Update feeds + echo "πŸ“‹ Updating feeds..." + ./scripts/feeds update -a + ./scripts/feeds install -a + + # Configure SDK + echo "βš™οΈ Configuring SDK..." + make defconfig + + - name: Copy packages to SDK + run: | + echo "πŸ“ Copying SecuBox packages to SDK..." + + # List of our packages + PACKAGES=( + "luci-app-crowdsec-dashboard" + "luci-app-netdata-dashboard" + "luci-app-netifyd-dashboard" + "luci-app-wireguard-dashboard" + "luci-app-network-modes" + "luci-app-client-guardian" + "luci-app-system-hub" + ) + + # Create package directory + mkdir -p ~/sdk/package/secubox + + # Copy each package if it exists + for pkg in "${PACKAGES[@]}"; do + if [[ -d "$GITHUB_WORKSPACE/$pkg" ]]; then + echo " πŸ“¦ $pkg" + cp -r "$GITHUB_WORKSPACE/$pkg" ~/sdk/package/secubox/ + else + echo " ⚠️ $pkg not found in repository" + fi + done + + # If packages are in a subdirectory + if [[ -d "$GITHUB_WORKSPACE/packages" ]]; then + cp -r "$GITHUB_WORKSPACE/packages/"* ~/sdk/package/secubox/ 2>/dev/null || true + fi + + # List what we have + echo "πŸ“‹ Packages in SDK:" + ls -la ~/sdk/package/secubox/ || echo " (empty)" + + - name: Update package version + run: | + VERSION="${{ needs.setup.outputs.version }}" + echo "πŸ“ Setting package version to: $VERSION" + + # Update Makefile version in each package + for makefile in ~/sdk/package/secubox/*/Makefile; do + if [[ -f "$makefile" ]]; then + sed -i "s/PKG_VERSION:=.*/PKG_VERSION:=$VERSION/" "$makefile" + sed -i "s/PKG_RELEASE:=.*/PKG_RELEASE:=1/" "$makefile" + echo " βœ… Updated: $(dirname $makefile | xargs basename)" + fi + done + + - name: Build packages + run: | + cd ~/sdk + + echo "πŸ”¨ Building SecuBox packages for ${{ matrix.description }}..." + + # Enable our packages + for pkg in ~/sdk/package/secubox/*/; do + PKG_NAME=$(basename "$pkg") + echo "CONFIG_PACKAGE_${PKG_NAME}=m" >> .config + done + + make defconfig + + # Build with verbose output on error + make package/secubox/compile V=s -j$(nproc) || { + echo "❌ Build failed, retrying with single thread..." + make package/secubox/compile V=s -j1 + } + + # Generate package index + make package/index V=s + + - name: Collect artifacts + id: collect + run: | + echo "πŸ“¦ Collecting built packages..." + + mkdir -p $GITHUB_WORKSPACE/artifacts/${{ matrix.target }} + + # Find and copy .ipk files + find ~/sdk/bin -name "*.ipk" -exec cp {} $GITHUB_WORKSPACE/artifacts/${{ matrix.target }}/ \; + + # Copy package index + find ~/sdk/bin -name "Packages*" -exec cp {} $GITHUB_WORKSPACE/artifacts/${{ matrix.target }}/ \; 2>/dev/null || true + + # List artifacts + echo "πŸ“‹ Built packages for ${{ matrix.target }}:" + ls -la $GITHUB_WORKSPACE/artifacts/${{ matrix.target }}/ + + # Count packages + PKG_COUNT=$(find $GITHUB_WORKSPACE/artifacts/${{ matrix.target }} -name "*.ipk" | wc -l) + echo "pkg_count=$PKG_COUNT" >> $GITHUB_OUTPUT + + if [[ $PKG_COUNT -eq 0 ]]; then + echo "⚠️ No packages built!" + exit 1 + fi + + - name: Create checksums + run: | + cd $GITHUB_WORKSPACE/artifacts/${{ matrix.target }} + sha256sum *.ipk > SHA256SUMS + echo "βœ… Checksums created" + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: packages-${{ matrix.target }} + path: artifacts/${{ matrix.target }}/ + retention-days: 30 + + # ============================================ + # Create combined release + # ============================================ + release: + needs: [setup, build] + runs-on: ubuntu-latest + if: startsWith(github.ref, 'refs/tags/v') + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: packages + pattern: packages-* + + - name: Organize packages + run: | + echo "πŸ“ Organizing release packages..." + + VERSION="${{ needs.setup.outputs.version }}" + mkdir -p release + + # Create architecture-specific archives + for arch_dir in packages/packages-*/; do + ARCH=$(basename "$arch_dir" | sed 's/packages-//') + echo "πŸ“¦ Processing $ARCH..." + + # Create tarball + tar -czf "release/secubox-${VERSION}-${ARCH}.tar.gz" -C "$arch_dir" . + + # Copy individual .ipk files to flat structure + mkdir -p "release/ipk/${ARCH}" + cp "$arch_dir"/*.ipk "release/ipk/${ARCH}/" 2>/dev/null || true + done + + # Create "all architectures" mega-archive + tar -czf "release/secubox-${VERSION}-all-architectures.tar.gz" -C packages . + + # Create release notes + cat > release/RELEASE_NOTES.md << EOF + # SecuBox $VERSION + + ## πŸ“¦ Packages Included + + - luci-app-crowdsec-dashboard - CrowdSec Security Dashboard + - luci-app-netdata-dashboard - Netdata Monitoring Dashboard + - luci-app-netifyd-dashboard - Netifyd DPI Dashboard + - luci-app-wireguard-dashboard - WireGuard VPN Dashboard + - luci-app-network-modes - Network Mode Switcher + - luci-app-client-guardian - NAC & Captive Portal + - luci-app-system-hub - System Hub Control Center + + ## πŸ—οΈ Supported Architectures + + | Target | Architecture | Description | + |--------|--------------|-------------| + | x86-64 | x86_64 | PC, VMs, Containers | + | aarch64-cortex-a53 | aarch64 | ESPRESSObin, Sheeva64 | + | aarch64-cortex-a72 | aarch64 | MOCHAbin, RPi4 | + | arm-cortex-a7 | arm | Orange Pi, Banana Pi | + | arm-cortex-a9 | arm | Linksys WRT, Turris | + | mips-24kc | mips | TP-Link, Ubiquiti | + | mipsel-24kc | mipsel | Xiaomi, GL.iNet | + | mediatek-filogic | aarch64 | MT7981, MT7986 | + | qualcomm-ipq40xx | arm | Google WiFi | + | rockchip-armv8 | aarch64 | NanoPi R4S, R5S | + + ## πŸ“₯ Installation + + \`\`\`bash + # Download package for your architecture + opkg update + opkg install luci-app-crowdsec-dashboard_${VERSION}_*.ipk + # ... install other packages as needed + \`\`\` + + ## πŸ”— Links + + - [Documentation](https://cybermind.fr/docs/secubox) + - [GitHub](https://github.com/gkerma) + - [CyberMind.fr](https://cybermind.fr) + + --- + Built with OpenWrt SDK ${{ env.OPENWRT_VERSION }} + EOF + + echo "βœ… Release organized" + ls -la release/ + + - name: Create global checksums + run: | + cd release + sha256sum *.tar.gz > SHA256SUMS + echo "βœ… Global checksums created" + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + name: SecuBox ${{ needs.setup.outputs.version }} + body_path: release/RELEASE_NOTES.md + files: | + release/*.tar.gz + release/SHA256SUMS + draft: false + prerelease: ${{ contains(github.ref, 'alpha') || contains(github.ref, 'beta') || contains(github.ref, 'rc') }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # ============================================ + # Build status summary + # ============================================ + summary: + needs: [setup, build] + runs-on: ubuntu-latest + if: always() + + steps: + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: packages + pattern: packages-* + continue-on-error: true + + - name: Generate build summary + run: | + echo "# πŸ“Š SecuBox Build Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Version:** ${{ needs.setup.outputs.version }}" >> $GITHUB_STEP_SUMMARY + echo "**OpenWrt:** ${{ env.OPENWRT_VERSION }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "## Build Results" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| Architecture | Status | Packages |" >> $GITHUB_STEP_SUMMARY + echo "|--------------|--------|----------|" >> $GITHUB_STEP_SUMMARY + + for arch_dir in packages/packages-*/; do + if [[ -d "$arch_dir" ]]; then + ARCH=$(basename "$arch_dir" | sed 's/packages-//') + PKG_COUNT=$(find "$arch_dir" -name "*.ipk" 2>/dev/null | wc -l) + if [[ $PKG_COUNT -gt 0 ]]; then + echo "| $ARCH | βœ… Success | $PKG_COUNT |" >> $GITHUB_STEP_SUMMARY + else + echo "| $ARCH | ⚠️ No packages | 0 |" >> $GITHUB_STEP_SUMMARY + fi + fi + done + + echo "" >> $GITHUB_STEP_SUMMARY + echo "## πŸ“¦ Artifacts" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Download artifacts from the Actions tab above." >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/build-secubox-images.yml b/.github/workflows/build-secubox-images.yml new file mode 100644 index 00000000..a9b5c969 --- /dev/null +++ b/.github/workflows/build-secubox-images.yml @@ -0,0 +1,398 @@ +name: Build SecuBox Images (GlobalScale) + +on: + workflow_dispatch: + inputs: + device: + description: 'Target device' + required: true + type: choice + options: + - espressobin-v7 + - espressobin-ultra + - sheeva64 + - sheeva64-wifi + - mochabin + - all + openwrt_version: + description: 'OpenWrt version' + required: true + default: '23.05.5' + type: choice + options: + - '23.05.5' + - '23.05.4' + - 'SNAPSHOT' + include_secubox: + description: 'Include SecuBox packages' + required: true + type: boolean + default: true + +env: + OPENWRT_VERSION: ${{ github.event.inputs.openwrt_version }} + +jobs: + # ============================================ + # Build firmware images for GlobalScale devices + # ============================================ + build-image: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - device: espressobin-v7 + target: mvebu + subtarget: cortexa53 + profile: globalscale_espressobin + description: "ESPRESSObin V7 (1-2GB DDR4)" + enabled: ${{ github.event.inputs.device == 'espressobin-v7' || github.event.inputs.device == 'all' }} + + - device: espressobin-ultra + target: mvebu + subtarget: cortexa53 + profile: globalscale_espressobin-ultra + description: "ESPRESSObin Ultra (PoE, WiFi)" + enabled: ${{ github.event.inputs.device == 'espressobin-ultra' || github.event.inputs.device == 'all' }} + + - device: sheeva64 + target: mvebu + subtarget: cortexa53 + profile: globalscale_sheeva64 + description: "Sheeva64 (Plug computer)" + enabled: ${{ github.event.inputs.device == 'sheeva64' || github.event.inputs.device == 'all' }} + + - device: sheeva64-wifi + target: mvebu + subtarget: cortexa53 + profile: globalscale_sheeva64 + description: "Sheeva64 WiFi (802.11ac + BT)" + enabled: ${{ github.event.inputs.device == 'sheeva64-wifi' || github.event.inputs.device == 'all' }} + + - device: mochabin + target: mvebu + subtarget: cortexa72 + profile: globalscale_mochabin + description: "MOCHAbin (Quad-core A72, 10G)" + enabled: ${{ github.event.inputs.device == 'mochabin' || github.event.inputs.device == 'all' }} + + name: ${{ matrix.description }} + if: ${{ matrix.enabled != 'false' }} + + steps: + - name: Checkout SecuBox packages + uses: actions/checkout@v4 + + - name: Free disk space + run: | + echo "🧹 Cleaning up disk space..." + sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc + sudo docker image prune --all --force + df -h + + - 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-distutils python3-setuptools rsync \ + swig unzip zlib1g-dev file wget curl qemu-utils + + - name: Clone OpenWrt + run: | + if [[ "${{ env.OPENWRT_VERSION }}" == "SNAPSHOT" ]]; then + git clone --depth 1 https://github.com/openwrt/openwrt.git openwrt + else + git clone --depth 1 --branch v${{ env.OPENWRT_VERSION }} \ + https://github.com/openwrt/openwrt.git openwrt + fi + + - name: Update feeds + run: | + cd openwrt + ./scripts/feeds update -a + ./scripts/feeds install -a + + - name: Copy SecuBox packages + if: ${{ github.event.inputs.include_secubox == 'true' }} + run: | + mkdir -p openwrt/package/secubox + + for pkg in luci-app-*/; do + if [[ -d "$pkg" ]]; then + echo "πŸ“¦ Adding $pkg" + cp -r "$pkg" openwrt/package/secubox/ + fi + done + + - name: Generate SecuBox config + run: | + cd openwrt + + # Base configuration + cat > .config << EOF + # Target + CONFIG_TARGET_${{ matrix.target }}=y + CONFIG_TARGET_${{ matrix.target }}_${{ matrix.subtarget }}=y + CONFIG_TARGET_${{ matrix.target }}_${{ matrix.subtarget }}_DEVICE_${{ matrix.profile }}=y + + # Image settings + CONFIG_TARGET_ROOTFS_SQUASHFS=y + CONFIG_TARGET_ROOTFS_EXT4FS=y + CONFIG_TARGET_KERNEL_PARTSIZE=32 + CONFIG_TARGET_ROOTFS_PARTSIZE=512 + + # Base packages + CONFIG_PACKAGE_luci=y + CONFIG_PACKAGE_luci-ssl=y + CONFIG_PACKAGE_luci-app-opkg=y + CONFIG_PACKAGE_luci-theme-openwrt-2020=y + + # Networking essentials + CONFIG_PACKAGE_curl=y + CONFIG_PACKAGE_wget-ssl=y + CONFIG_PACKAGE_iptables=y + CONFIG_PACKAGE_ip6tables=y + CONFIG_PACKAGE_kmod-nft-core=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 + CONFIG_PACKAGE_block-mount=y + + # Wireless (if applicable) + CONFIG_PACKAGE_hostapd-common=y + CONFIG_PACKAGE_wpad-basic-mbedtls=y + + # Monitoring tools + CONFIG_PACKAGE_htop=y + CONFIG_PACKAGE_iftop=y + CONFIG_PACKAGE_tcpdump=y + + # SSH + CONFIG_PACKAGE_openssh-sftp-server=y + EOF + + - name: Add SecuBox packages to config + if: ${{ github.event.inputs.include_secubox == 'true' }} + run: | + cd openwrt + + # CrowdSec + cat >> .config << EOF + CONFIG_PACKAGE_crowdsec=y + CONFIG_PACKAGE_crowdsec-firewall-bouncer=y + CONFIG_PACKAGE_luci-app-crowdsec-dashboard=y + EOF + + # Netdata + cat >> .config << EOF + CONFIG_PACKAGE_netdata=y + CONFIG_PACKAGE_luci-app-netdata-dashboard=y + EOF + + # Netifyd + cat >> .config << EOF + CONFIG_PACKAGE_netifyd=y + CONFIG_PACKAGE_luci-app-netifyd-dashboard=y + EOF + + # WireGuard + cat >> .config << EOF + CONFIG_PACKAGE_wireguard-tools=y + CONFIG_PACKAGE_kmod-wireguard=y + CONFIG_PACKAGE_luci-app-wireguard-dashboard=y + CONFIG_PACKAGE_qrencode=y + EOF + + # SecuBox core + cat >> .config << EOF + CONFIG_PACKAGE_luci-app-network-modes=y + CONFIG_PACKAGE_luci-app-client-guardian=y + CONFIG_PACKAGE_luci-app-system-hub=y + EOF + + - name: Add device-specific packages + run: | + cd openwrt + + case "${{ matrix.device }}" in + mochabin) + # 10G networking, more RAM + cat >> .config << EOF + CONFIG_PACKAGE_kmod-sfp=y + CONFIG_PACKAGE_kmod-phy-marvell-10g=y + CONFIG_PACKAGE_prometheus-node-exporter-lua=y + EOF + ;; + + espressobin-ultra|sheeva64-wifi) + # WiFi support + cat >> .config << EOF + CONFIG_PACKAGE_kmod-mt76=y + CONFIG_PACKAGE_kmod-mac80211=y + EOF + ;; + + sheeva64*) + # Minimal for plug computer + cat >> .config << EOF + # Optimized for plug form factor + CONFIG_PACKAGE_kmod-ledtrig-heartbeat=y + EOF + ;; + esac + + - name: Make defconfig + run: | + cd openwrt + make defconfig + + - name: Download packages + run: | + cd openwrt + make download -j$(nproc) V=s || make download -j1 V=s + + - name: Build firmware + run: | + cd openwrt + + echo "πŸ”¨ Building firmware for ${{ matrix.description }}..." + echo "⏱️ This may take 1-2 hours..." + + make -j$(nproc) V=s 2>&1 | tee build.log || { + echo "❌ Build failed, retrying with single thread..." + make -j1 V=s 2>&1 | tee build-retry.log + } + + - name: Prepare artifacts + run: | + mkdir -p artifacts + + # Copy firmware images + find openwrt/bin/targets -name "*.img.gz" -exec cp {} artifacts/ \; + find openwrt/bin/targets -name "*.bin" -exec cp {} artifacts/ \; + find openwrt/bin/targets -name "*sysupgrade*" -exec cp {} artifacts/ \; + find openwrt/bin/targets -name "*factory*" -exec cp {} artifacts/ \; + + # Copy packages + mkdir -p artifacts/packages + find openwrt/bin/packages -name "luci-app-*secubox*.ipk" -exec cp {} artifacts/packages/ \; 2>/dev/null || true + find openwrt/bin/packages -name "luci-app-*dashboard*.ipk" -exec cp {} artifacts/packages/ \; 2>/dev/null || true + + # Generate checksums + cd artifacts + sha256sum * > SHA256SUMS 2>/dev/null || true + + # Create info file + cat > BUILD_INFO.txt << EOF + SecuBox Firmware Build + ======================= + Device: ${{ matrix.description }} + Profile: ${{ matrix.profile }} + Target: ${{ matrix.target }}/${{ matrix.subtarget }} + OpenWrt: ${{ env.OPENWRT_VERSION }} + SecuBox: ${{ github.event.inputs.include_secubox }} + Built: $(date -u +%Y-%m-%dT%H:%M:%SZ) + Commit: ${{ github.sha }} + EOF + + echo "πŸ“¦ Artifacts:" + ls -la + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: secubox-${{ matrix.device }}-${{ env.OPENWRT_VERSION }} + path: artifacts/ + retention-days: 30 + + # ============================================ + # Create combined release for all devices + # ============================================ + release: + needs: build-image + runs-on: ubuntu-latest + if: github.event.inputs.device == 'all' + + steps: + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: firmware + pattern: secubox-* + + - name: Organize release + run: | + mkdir -p release + + for device_dir in firmware/secubox-*/; do + DEVICE=$(basename "$device_dir" | sed 's/secubox-//' | sed "s/-${{ env.OPENWRT_VERSION }}//") + echo "πŸ“¦ Processing $DEVICE..." + + # Create device archive + tar -czf "release/secubox-firmware-${DEVICE}.tar.gz" -C "$device_dir" . + done + + # Global checksums + cd release + sha256sum *.tar.gz > SHA256SUMS + + # Release notes + cat > RELEASE_NOTES.md << 'EOF' + # SecuBox Firmware Images + + Pre-built firmware images for GlobalScale devices with SecuBox modules pre-installed. + + ## Included Devices + + | Device | SoC | RAM | Description | + |--------|-----|-----|-------------| + | ESPRESSObin V7 | Armada 3720 | 1-2GB | Entry-level | + | ESPRESSObin Ultra | Armada 3720 | 1-2GB | WiFi + PoE | + | Sheeva64 | Armada 3720 | 1GB | Plug computer | + | MOCHAbin | Armada 7040 | 4-8GB | Quad-core + 10G | + + ## Pre-installed SecuBox Modules + + - luci-app-crowdsec-dashboard + - luci-app-netdata-dashboard + - luci-app-netifyd-dashboard + - luci-app-wireguard-dashboard + - luci-app-network-modes + - luci-app-client-guardian + - luci-app-system-hub + + ## Installation + + 1. Download the appropriate firmware for your device + 2. Flash using OpenWrt sysupgrade or manufacturer tools + 3. Access LuCI at http://192.168.1.1 + 4. Navigate to Services β†’ SecuBox + + ## Support + + - [Documentation](https://cybermind.fr/docs/secubox) + - [CyberMind.fr](https://cybermind.fr) + EOF + + - name: Create release + if: github.ref == 'refs/heads/main' + uses: softprops/action-gh-release@v2 + with: + name: "SecuBox Firmware ${{ env.OPENWRT_VERSION }}" + tag_name: "firmware-${{ env.OPENWRT_VERSION }}-${{ github.run_number }}" + body_path: release/RELEASE_NOTES.md + files: | + release/*.tar.gz + release/SHA256SUMS + draft: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/test-validate.yml b/.github/workflows/test-validate.yml new file mode 100644 index 00000000..6a923ee3 --- /dev/null +++ b/.github/workflows/test-validate.yml @@ -0,0 +1,327 @@ +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 */Makefile; do + if [[ -f "$makefile" ]]; then + PKG=$(dirname "$makefile") + echo " πŸ” Checking $PKG..." + + # Required fields + REQUIRED_FIELDS=( + "PKG_NAME" + "PKG_VERSION" + "PKG_RELEASE" + "PKG_LICENSE" + "LUCI_TITLE" + ) + + 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 + + # Find all JSON files + 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..." + + # Install node for syntax check + 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) + + 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..." + + ERRORS=0 + + # Check RPCD scripts + while IFS= read -r script; do + echo " πŸ” Checking $script..." + if ! shellcheck -s sh "$script"; then + ERRORS=$((ERRORS + 1)) + fi + done < <(find . -path "*/rpcd/*" -type f -executable) + + # Check init scripts + while IFS= read -r script; do + echo " πŸ” Checking $script..." + if ! shellcheck -s sh "$script"; then + ERRORS=$((ERRORS + 1)) + fi + done < <(find . -path "*/init.d/*" -type f) + + if [[ $ERRORS -gt 0 ]]; then + echo "⚠️ Found $ERRORS 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" + 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" + ERRORS=$((ERRORS + 1)) + fi + done < <(find . -path "*/etc/init.d/*" -type f 2>/dev/null) + + if [[ $ERRORS -gt 0 ]]; then + echo "❌ Found $ERRORS permission errors" + exit 1 + fi + echo "βœ… File permissions correct" + + - name: Validate package structure + run: | + echo "πŸ“‹ Validating package structure..." + + for pkg in luci-app-*/; do + if [[ -d "$pkg" ]]; then + echo " πŸ“¦ Checking $pkg..." + + # Required directories/files + REQUIRED=( + "Makefile" + ) + + # Optional but recommended + RECOMMENDED=( + "htdocs/luci-static/resources" + "root/usr/share/luci/menu.d" + "root/usr/share/rpcd/acl.d" + ) + + for req in "${REQUIRED[@]}"; do + if [[ ! -e "${pkg}${req}" ]]; then + echo " ❌ Missing required: $req" + exit 1 + fi + done + + 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-distutils 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 + + - 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: | + mkdir -p ~/sdk/package/secubox + + for pkg in luci-app-*/; do + if [[ -d "$pkg" ]]; then + cp -r "$pkg" ~/sdk/package/secubox/ + fi + done + + - name: Build test + run: | + cd ~/sdk + + # Enable packages + for pkg in ~/sdk/package/secubox/*/; do + PKG_NAME=$(basename "$pkg") + echo "CONFIG_PACKAGE_${PKG_NAME}=m" >> .config + done + + make defconfig + + # Build with timeout + timeout 30m make package/secubox/compile V=s -j$(nproc) || { + echo "⚠️ Build timeout or error" + exit 1 + } + + - name: Verify output + run: | + echo "πŸ“‹ Built packages:" + find ~/sdk/bin -name "*.ipk" -exec ls -la {} \; + + PKG_COUNT=$(find ~/sdk/bin -name "*.ipk" | wc -l) + echo "πŸ“¦ Total packages built: $PKG_COUNT" + + if [[ $PKG_COUNT -eq 0 ]]; then + echo "❌ No packages were built!" + exit 1 + fi + + # ============================================ + # 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 "| 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-) + + echo "| $PKG_NAME | $PKG_VERSION | $PKG_TITLE |" >> PACKAGES.md + fi + done + + echo "" >> PACKAGES.md + echo "Generated: $(date -u +%Y-%m-%dT%H:%M:%SZ)" >> PACKAGES.md + + cat PACKAGES.md + + - name: Upload docs + uses: actions/upload-artifact@v4 + with: + name: documentation + path: PACKAGES.md diff --git a/README.md b/README.md new file mode 100644 index 00000000..73af6fe8 --- /dev/null +++ b/README.md @@ -0,0 +1,232 @@ +# SecuBox OpenWrt CI/CD + +[![Build OpenWrt Packages](https://github.com/gkerma/secubox/actions/workflows/build-openwrt-packages.yml/badge.svg)](https://github.com/gkerma/secubox/actions/workflows/build-openwrt-packages.yml) +[![Test & Validate](https://github.com/gkerma/secubox/actions/workflows/test-validate.yml/badge.svg)](https://github.com/gkerma/secubox/actions/workflows/test-validate.yml) + +## 🎯 Overview + +Ce dΓ©pΓ΄t contient les workflows GitHub Actions pour compiler automatiquement les packages SecuBox LuCI pour **toutes les architectures OpenWrt supportΓ©es**. + +## πŸ“¦ Packages CompilΓ©s + +| Package | Description | +|---------|-------------| +| `luci-app-crowdsec-dashboard` | Dashboard CrowdSec | +| `luci-app-netdata-dashboard` | Dashboard Netdata | +| `luci-app-netifyd-dashboard` | Dashboard Netifyd DPI | +| `luci-app-wireguard-dashboard` | Dashboard WireGuard VPN | +| `luci-app-network-modes` | Modes rΓ©seau (Router/Bridge/AP) | +| `luci-app-client-guardian` | NAC & Portail Captif | +| `luci-app-system-hub` | Centre de contrΓ΄le unifiΓ© | + +## πŸ—οΈ Architectures SupportΓ©es + +### ARM 64-bit (AArch64) + +| Target | Architecture | Devices | +|--------|--------------|---------| +| `aarch64-cortex-a53` | Cortex-A53 | **ESPRESSObin**, **Sheeva64**, BananaPi R64 | +| `aarch64-cortex-a72` | Cortex-A72 | **MOCHAbin**, Raspberry Pi 4, NanoPi R4S | +| `aarch64-generic` | Generic ARMv8 | Rock64, Pine64, QEMU ARM64 | +| `mediatek-filogic` | MT7981/MT7986 | GL.iNet MT3000, BananaPi R3 | +| `rockchip-armv8` | RK3328/RK3399 | NanoPi R4S, R5S, FriendlyARM | +| `bcm27xx-bcm2711` | BCM2711 | Raspberry Pi 4, Compute Module 4 | + +### ARM 32-bit + +| Target | Architecture | Devices | +|--------|--------------|---------| +| `arm-cortex-a7-neon` | Cortex-A7 | Orange Pi, BananaPi, Allwinner | +| `arm-cortex-a9-neon` | Cortex-A9 | Linksys WRT, Turris Omnia | +| `arm-cortex-a15-neon` | Cortex-A15 | QEMU ARM | +| `qualcomm-ipq40xx` | IPQ40xx | Google WiFi, Zyxel NBG6617 | +| `qualcomm-ipq806x` | IPQ806x | Netgear R7800, R7500 | + +### MIPS + +| Target | Architecture | Devices | +|--------|--------------|---------| +| `mips-24kc` | MIPS 24Kc | TP-Link Archer, Ubiquiti | +| `mipsel-24kc` | MIPS LE 24Kc | Xiaomi, GL.iNet, Netgear | +| `mipsel-74kc` | MIPS LE 74Kc | Broadcom BCM47xx | + +### x86 + +| Target | Architecture | Devices | +|--------|--------------|---------| +| `x86-64` | x86_64 | PC, VMs, Docker, Proxmox | +| `x86-generic` | i386 | Legacy PC, old Atom | + +## πŸš€ Utilisation + +### Compilation Automatique + +Les packages sont compilΓ©s automatiquement lors de : + +1. **Push sur `main`/`master`** : Compilation de test +2. **Pull Request** : Validation et test +3. **Tag `v*`** : CrΓ©ation de release avec tous les packages + +### Compilation Manuelle + +1. Aller dans **Actions** β†’ **Build OpenWrt Packages** +2. Cliquer sur **Run workflow** +3. SΓ©lectionner : + - **OpenWrt version** : 23.05.5, 22.03.7, ou SNAPSHOT + - **Architectures** : `all` ou liste sΓ©parΓ©e par virgules + +``` +# Exemples d'architectures +all # Toutes les architectures +x86-64 # Uniquement x86_64 +aarch64-cortex-a53,aarch64-cortex-a72 # GlobalScale devices +mips-24kc,mipsel-24kc # MIPS routeurs +``` + +### TΓ©lΓ©chargement des Artifacts + +1. Aller dans **Actions** β†’ SΓ©lectionner un workflow +2. Cliquer sur le run souhaitΓ© +3. TΓ©lΓ©charger les **Artifacts** en bas de page + +Les artifacts sont organisΓ©s par architecture : +``` +packages-x86-64/ + β”œβ”€β”€ luci-app-crowdsec-dashboard_1.0.0-1_all.ipk + β”œβ”€β”€ luci-app-netdata-dashboard_1.0.0-1_all.ipk + β”œβ”€β”€ ... + └── SHA256SUMS +``` + +## πŸ“ Structure du DΓ©pΓ΄t + +``` +secubox/ +β”œβ”€β”€ .github/ +β”‚ └── workflows/ +β”‚ β”œβ”€β”€ build-openwrt-packages.yml # Build principal +β”‚ └── test-validate.yml # Tests & validation +β”œβ”€β”€ luci-app-crowdsec-dashboard/ +β”‚ β”œβ”€β”€ Makefile +β”‚ β”œβ”€β”€ htdocs/luci-static/resources/ +β”‚ β”‚ β”œβ”€β”€ view/crowdsec/ # JavaScript views +β”‚ β”‚ └── crowdsec/ # API & CSS +β”‚ └── root/ +β”‚ β”œβ”€β”€ etc/config/ # UCI config +β”‚ └── usr/ +β”‚ β”œβ”€β”€ libexec/rpcd/ # RPCD backend +β”‚ └── share/ +β”‚ β”œβ”€β”€ luci/menu.d/ # Menu JSON +β”‚ └── rpcd/acl.d/ # ACL JSON +β”œβ”€β”€ luci-app-netdata-dashboard/ +β”œβ”€β”€ luci-app-netifyd-dashboard/ +β”œβ”€β”€ luci-app-wireguard-dashboard/ +β”œβ”€β”€ luci-app-network-modes/ +β”œβ”€β”€ luci-app-client-guardian/ +β”œβ”€β”€ luci-app-system-hub/ +└── README.md +``` + +## πŸ”§ CrΓ©er un Nouveau Package + +1. Copier le template : +```bash +cp -r templates/luci-app-template luci-app-nouveau +``` + +2. Γ‰diter `Makefile` : +```makefile +PKG_NAME:=luci-app-nouveau +PKG_VERSION:=1.0.0 +LUCI_TITLE:=Mon Nouveau Dashboard +LUCI_DEPENDS:=+luci-base +nouveau-backend +``` + +3. CrΓ©er les fichiers requis : +```bash +luci-app-nouveau/ +β”œβ”€β”€ Makefile +β”œβ”€β”€ htdocs/luci-static/resources/ +β”‚ β”œβ”€β”€ view/nouveau/ +β”‚ β”‚ └── overview.js +β”‚ └── nouveau/ +β”‚ β”œβ”€β”€ api.js +β”‚ └── dashboard.css +└── root/ + └── usr/share/ + β”œβ”€β”€ luci/menu.d/luci-app-nouveau.json + └── rpcd/acl.d/luci-app-nouveau.json +``` + +4. Commit et push : +```bash +git add luci-app-nouveau/ +git commit -m "feat: add luci-app-nouveau" +git push +``` + +## 🏷️ CrΓ©er une Release + +```bash +# CrΓ©er un tag versionnΓ© +git tag -a v1.2.0 -m "Release 1.2.0" +git push origin v1.2.0 +``` + +La release sera créée automatiquement avec : +- Archives `.tar.gz` par architecture +- Archive globale toutes architectures +- Checksums SHA256 +- Notes de release gΓ©nΓ©rΓ©es + +## βš™οΈ Configuration CI + +### Variables d'Environnement + +| Variable | Default | Description | +|----------|---------|-------------| +| `OPENWRT_VERSION` | `23.05.5` | Version OpenWrt SDK | + +### Secrets Requis + +Aucun secret requis pour la compilation. Le `GITHUB_TOKEN` par dΓ©faut suffit pour crΓ©er les releases. + +### Cache + +Le SDK OpenWrt est mis en cache par architecture pour accΓ©lΓ©rer les builds suivants. + +## πŸ§ͺ Tests & Validation + +Le workflow `test-validate.yml` vΓ©rifie : + +- βœ… Structure des Makefiles (champs requis) +- βœ… Syntaxe JSON (menu, ACL) +- βœ… Syntaxe JavaScript (views) +- βœ… Scripts shell (shellcheck) +- βœ… Permissions des fichiers +- βœ… Build test sur x86_64 + +## πŸ“Š Matrice de CompatibilitΓ© + +| OpenWrt | Status | Notes | +|---------|--------|-------| +| 24.10.x | πŸ”œ PrΓ©vu | En attente release | +| 23.05.x | βœ… SupportΓ© | RecommandΓ© | +| 22.03.x | βœ… SupportΓ© | LTS | +| 21.02.x | ⚠️ Partiel | Fin de support | +| SNAPSHOT | βœ… SupportΓ© | Instable | + +## πŸ”— Liens + +- [OpenWrt SDK Documentation](https://openwrt.org/docs/guide-developer/using_the_sdk) +- [LuCI Development Guide](https://github.com/openwrt/luci/wiki) +- [CyberMind.fr](https://cybermind.fr) +- [SecuBox Project](https://cybermind.fr/secubox) + +## πŸ“„ License + +Apache-2.0 Β© 2025 CyberMind.fr + +--- + +**Made with ❀️ in France πŸ‡«πŸ‡·** diff --git a/build-openwrt-packages.yml b/build-openwrt-packages.yml new file mode 100644 index 00000000..caa2f1ad --- /dev/null +++ b/build-openwrt-packages.yml @@ -0,0 +1,551 @@ +name: Build OpenWrt Packages + +on: + push: + branches: [main, master, develop] + tags: + - 'v*' + pull_request: + branches: [main, master] + workflow_dispatch: + inputs: + openwrt_version: + description: 'OpenWrt version' + required: true + default: '23.05.5' + type: choice + options: + - '23.05.5' + - '23.05.4' + - '22.03.7' + - 'SNAPSHOT' + architectures: + description: 'Architectures to build (comma-separated or "all")' + required: false + default: 'all' + +env: + OPENWRT_VERSION: ${{ github.event.inputs.openwrt_version || '23.05.5' }} + +jobs: + # ============================================ + # Determine build matrix + # ============================================ + setup: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + version: ${{ steps.version.outputs.version }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Determine version + id: version + run: | + if [[ "${{ github.ref }}" == refs/tags/v* ]]; then + VERSION="${{ github.ref_name }}" + else + VERSION="0.0.0-$(git rev-parse --short HEAD)" + fi + echo "version=${VERSION#v}" >> $GITHUB_OUTPUT + echo "πŸ“¦ Package version: ${VERSION#v}" + + - name: Set build matrix + id: set-matrix + run: | + # Full architecture matrix for OpenWrt + # Format: target/subtarget -> SDK name mapping + ALL_TARGETS=$(cat << 'EOF' + { + "include": [ + { + "target": "x86-64", + "arch": "x86_64", + "sdk_name": "x86-64", + "description": "x86 64-bit (PC, VM, containers)" + }, + { + "target": "x86-generic", + "arch": "i386_pentium4", + "sdk_name": "x86-generic", + "description": "x86 32-bit (legacy PC)" + }, + { + "target": "aarch64-generic", + "arch": "aarch64_generic", + "sdk_name": "armsr-armv8", + "description": "ARM 64-bit generic (RPi4, Rock64)" + }, + { + "target": "aarch64-cortex-a53", + "arch": "aarch64_cortex-a53", + "sdk_name": "mvebu-cortexa53", + "description": "ARM Cortex-A53 (ESPRESSObin, Sheeva64)" + }, + { + "target": "aarch64-cortex-a72", + "arch": "aarch64_cortex-a72", + "sdk_name": "mvebu-cortexa72", + "description": "ARM Cortex-A72 (MOCHAbin, MACCHIATObin)" + }, + { + "target": "arm-cortex-a7-neon", + "arch": "arm_cortex-a7_neon-vfpv4", + "sdk_name": "sunxi-cortexa7", + "description": "ARM Cortex-A7 (Orange Pi, Banana Pi)" + }, + { + "target": "arm-cortex-a9-neon", + "arch": "arm_cortex-a9_neon", + "sdk_name": "mvebu-cortexa9", + "description": "ARM Cortex-A9 (Linksys WRT, Turris)" + }, + { + "target": "arm-cortex-a15-neon", + "arch": "arm_cortex-a15_neon-vfpv4", + "sdk_name": "armvirt-32", + "description": "ARM Cortex-A15 (QEMU ARM)" + }, + { + "target": "mips-24kc", + "arch": "mips_24kc", + "sdk_name": "ath79-generic", + "description": "MIPS 24Kc (TP-Link, Ubiquiti)" + }, + { + "target": "mipsel-24kc", + "arch": "mipsel_24kc", + "sdk_name": "ramips-mt7621", + "description": "MIPS Little-Endian (Xiaomi, GL.iNet)" + }, + { + "target": "mipsel-74kc", + "arch": "mipsel_74kc", + "sdk_name": "bcm47xx-mips74k", + "description": "MIPS 74Kc (Broadcom routers)" + }, + { + "target": "mediatek-filogic", + "arch": "aarch64_cortex-a53", + "sdk_name": "mediatek-filogic", + "description": "MediaTek Filogic (MT7981, MT7986)" + }, + { + "target": "qualcomm-ipq40xx", + "arch": "arm_cortex-a7_neon-vfpv4", + "sdk_name": "ipq40xx-generic", + "description": "Qualcomm IPQ40xx (Google WiFi, Zyxel)" + }, + { + "target": "qualcomm-ipq806x", + "arch": "arm_cortex-a15_neon-vfpv4", + "sdk_name": "ipq806x-generic", + "description": "Qualcomm IPQ806x (Netgear R7800)" + }, + { + "target": "rockchip-armv8", + "arch": "aarch64_generic", + "sdk_name": "rockchip-armv8", + "description": "Rockchip (NanoPi R4S, R5S)" + }, + { + "target": "bcm27xx-bcm2711", + "arch": "aarch64_cortex-a72", + "sdk_name": "bcm27xx-bcm2711", + "description": "Raspberry Pi 4" + } + ] + } + EOF + ) + + INPUT_ARCHS="${{ github.event.inputs.architectures }}" + if [[ -z "$INPUT_ARCHS" || "$INPUT_ARCHS" == "all" ]]; then + MATRIX="$ALL_TARGETS" + else + # Filter matrix based on input + MATRIX=$(echo "$ALL_TARGETS" | jq -c --arg archs "$INPUT_ARCHS" ' + .include |= map(select(.target as $t | $archs | split(",") | map(gsub("^\\s+|\\s+$";"")) | any(. == $t or . == "all"))) + ') + fi + + echo "matrix=$MATRIX" >> $GITHUB_OUTPUT + echo "πŸ“‹ Build matrix:" + echo "$MATRIX" | jq '.' + + # ============================================ + # Build packages for each architecture + # ============================================ + build: + needs: setup + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.setup.outputs.matrix) }} + + name: Build ${{ matrix.target }} + + steps: + - name: Checkout source + uses: actions/checkout@v4 + + - name: Free disk space + run: | + echo "🧹 Cleaning up disk space..." + sudo rm -rf /usr/share/dotnet + sudo rm -rf /usr/local/lib/android + sudo rm -rf /opt/ghc + sudo rm -rf /opt/hostedtoolcache/CodeQL + sudo docker image prune --all --force + df -h + + - 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-distutils python3-setuptools rsync \ + swig unzip zlib1g-dev file wget curl jq + + - name: Cache OpenWrt SDK + uses: actions/cache@v4 + id: cache-sdk + with: + path: ~/sdk + key: openwrt-sdk-${{ env.OPENWRT_VERSION }}-${{ matrix.sdk_name }} + + - name: Download OpenWrt SDK + if: steps.cache-sdk.outputs.cache-hit != 'true' + run: | + echo "πŸ“₯ Downloading SDK for ${{ matrix.description }}..." + + VERSION="${{ env.OPENWRT_VERSION }}" + SDK_NAME="${{ matrix.sdk_name }}" + + if [[ "$VERSION" == "SNAPSHOT" ]]; then + BASE_URL="https://downloads.openwrt.org/snapshots/targets" + else + BASE_URL="https://downloads.openwrt.org/releases/${VERSION}/targets" + fi + + # Parse target/subtarget from sdk_name + TARGET=$(echo "$SDK_NAME" | cut -d'-' -f1) + SUBTARGET=$(echo "$SDK_NAME" | cut -d'-' -f2-) + + SDK_URL="${BASE_URL}/${TARGET}/${SUBTARGET}" + + echo "πŸ” Looking for SDK at: $SDK_URL" + + # Find SDK filename + SDK_FILE=$(curl -sL "$SDK_URL/" | grep -oP 'openwrt-sdk[^"]+\.tar\.(xz|zst)' | head -1) + + if [[ -z "$SDK_FILE" ]]; then + echo "⚠️ SDK not found, trying alternative URL pattern..." + SDK_FILE=$(curl -sL "$SDK_URL/sha256sums" | grep -oP 'openwrt-sdk[^\s]+' | head -1) + fi + + if [[ -z "$SDK_FILE" ]]; then + echo "❌ Could not find SDK for ${{ matrix.target }}" + echo "πŸ”— Checked: $SDK_URL" + exit 1 + fi + + echo "πŸ“¦ Downloading: $SDK_FILE" + wget -q --show-progress "${SDK_URL}/${SDK_FILE}" -O /tmp/sdk.tar.xz || \ + wget -q --show-progress "${SDK_URL}/${SDK_FILE}" -O /tmp/sdk.tar.zst + + mkdir -p ~/sdk + if [[ "$SDK_FILE" == *.zst ]]; then + zstd -d /tmp/sdk.tar.zst -o /tmp/sdk.tar + tar -xf /tmp/sdk.tar -C ~/sdk --strip-components=1 + else + tar -xf /tmp/sdk.tar.xz -C ~/sdk --strip-components=1 + fi + + echo "βœ… SDK extracted to ~/sdk" + + - name: Prepare SDK + run: | + cd ~/sdk + + # Update feeds + echo "πŸ“‹ Updating feeds..." + ./scripts/feeds update -a + ./scripts/feeds install -a + + # Configure SDK + echo "βš™οΈ Configuring SDK..." + make defconfig + + - name: Copy packages to SDK + run: | + echo "πŸ“ Copying SecuBox packages to SDK..." + + # List of our packages + PACKAGES=( + "luci-app-crowdsec-dashboard" + "luci-app-netdata-dashboard" + "luci-app-netifyd-dashboard" + "luci-app-wireguard-dashboard" + "luci-app-network-modes" + "luci-app-client-guardian" + "luci-app-system-hub" + ) + + # Create package directory + mkdir -p ~/sdk/package/secubox + + # Copy each package if it exists + for pkg in "${PACKAGES[@]}"; do + if [[ -d "$GITHUB_WORKSPACE/$pkg" ]]; then + echo " πŸ“¦ $pkg" + cp -r "$GITHUB_WORKSPACE/$pkg" ~/sdk/package/secubox/ + else + echo " ⚠️ $pkg not found in repository" + fi + done + + # If packages are in a subdirectory + if [[ -d "$GITHUB_WORKSPACE/packages" ]]; then + cp -r "$GITHUB_WORKSPACE/packages/"* ~/sdk/package/secubox/ 2>/dev/null || true + fi + + # List what we have + echo "πŸ“‹ Packages in SDK:" + ls -la ~/sdk/package/secubox/ || echo " (empty)" + + - name: Update package version + run: | + VERSION="${{ needs.setup.outputs.version }}" + echo "πŸ“ Setting package version to: $VERSION" + + # Update Makefile version in each package + for makefile in ~/sdk/package/secubox/*/Makefile; do + if [[ -f "$makefile" ]]; then + sed -i "s/PKG_VERSION:=.*/PKG_VERSION:=$VERSION/" "$makefile" + sed -i "s/PKG_RELEASE:=.*/PKG_RELEASE:=1/" "$makefile" + echo " βœ… Updated: $(dirname $makefile | xargs basename)" + fi + done + + - name: Build packages + run: | + cd ~/sdk + + echo "πŸ”¨ Building SecuBox packages for ${{ matrix.description }}..." + + # Enable our packages + for pkg in ~/sdk/package/secubox/*/; do + PKG_NAME=$(basename "$pkg") + echo "CONFIG_PACKAGE_${PKG_NAME}=m" >> .config + done + + make defconfig + + # Build with verbose output on error + make package/secubox/compile V=s -j$(nproc) || { + echo "❌ Build failed, retrying with single thread..." + make package/secubox/compile V=s -j1 + } + + # Generate package index + make package/index V=s + + - name: Collect artifacts + id: collect + run: | + echo "πŸ“¦ Collecting built packages..." + + mkdir -p $GITHUB_WORKSPACE/artifacts/${{ matrix.target }} + + # Find and copy .ipk files + find ~/sdk/bin -name "*.ipk" -exec cp {} $GITHUB_WORKSPACE/artifacts/${{ matrix.target }}/ \; + + # Copy package index + find ~/sdk/bin -name "Packages*" -exec cp {} $GITHUB_WORKSPACE/artifacts/${{ matrix.target }}/ \; 2>/dev/null || true + + # List artifacts + echo "πŸ“‹ Built packages for ${{ matrix.target }}:" + ls -la $GITHUB_WORKSPACE/artifacts/${{ matrix.target }}/ + + # Count packages + PKG_COUNT=$(find $GITHUB_WORKSPACE/artifacts/${{ matrix.target }} -name "*.ipk" | wc -l) + echo "pkg_count=$PKG_COUNT" >> $GITHUB_OUTPUT + + if [[ $PKG_COUNT -eq 0 ]]; then + echo "⚠️ No packages built!" + exit 1 + fi + + - name: Create checksums + run: | + cd $GITHUB_WORKSPACE/artifacts/${{ matrix.target }} + sha256sum *.ipk > SHA256SUMS + echo "βœ… Checksums created" + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: packages-${{ matrix.target }} + path: artifacts/${{ matrix.target }}/ + retention-days: 30 + + # ============================================ + # Create combined release + # ============================================ + release: + needs: [setup, build] + runs-on: ubuntu-latest + if: startsWith(github.ref, 'refs/tags/v') + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: packages + pattern: packages-* + + - name: Organize packages + run: | + echo "πŸ“ Organizing release packages..." + + VERSION="${{ needs.setup.outputs.version }}" + mkdir -p release + + # Create architecture-specific archives + for arch_dir in packages/packages-*/; do + ARCH=$(basename "$arch_dir" | sed 's/packages-//') + echo "πŸ“¦ Processing $ARCH..." + + # Create tarball + tar -czf "release/secubox-${VERSION}-${ARCH}.tar.gz" -C "$arch_dir" . + + # Copy individual .ipk files to flat structure + mkdir -p "release/ipk/${ARCH}" + cp "$arch_dir"/*.ipk "release/ipk/${ARCH}/" 2>/dev/null || true + done + + # Create "all architectures" mega-archive + tar -czf "release/secubox-${VERSION}-all-architectures.tar.gz" -C packages . + + # Create release notes + cat > release/RELEASE_NOTES.md << EOF + # SecuBox $VERSION + + ## πŸ“¦ Packages Included + + - luci-app-crowdsec-dashboard - CrowdSec Security Dashboard + - luci-app-netdata-dashboard - Netdata Monitoring Dashboard + - luci-app-netifyd-dashboard - Netifyd DPI Dashboard + - luci-app-wireguard-dashboard - WireGuard VPN Dashboard + - luci-app-network-modes - Network Mode Switcher + - luci-app-client-guardian - NAC & Captive Portal + - luci-app-system-hub - System Hub Control Center + + ## πŸ—οΈ Supported Architectures + + | Target | Architecture | Description | + |--------|--------------|-------------| + | x86-64 | x86_64 | PC, VMs, Containers | + | aarch64-cortex-a53 | aarch64 | ESPRESSObin, Sheeva64 | + | aarch64-cortex-a72 | aarch64 | MOCHAbin, RPi4 | + | arm-cortex-a7 | arm | Orange Pi, Banana Pi | + | arm-cortex-a9 | arm | Linksys WRT, Turris | + | mips-24kc | mips | TP-Link, Ubiquiti | + | mipsel-24kc | mipsel | Xiaomi, GL.iNet | + | mediatek-filogic | aarch64 | MT7981, MT7986 | + | qualcomm-ipq40xx | arm | Google WiFi | + | rockchip-armv8 | aarch64 | NanoPi R4S, R5S | + + ## πŸ“₯ Installation + + \`\`\`bash + # Download package for your architecture + opkg update + opkg install luci-app-crowdsec-dashboard_${VERSION}_*.ipk + # ... install other packages as needed + \`\`\` + + ## πŸ”— Links + + - [Documentation](https://cybermind.fr/docs/secubox) + - [GitHub](https://github.com/gkerma) + - [CyberMind.fr](https://cybermind.fr) + + --- + Built with OpenWrt SDK ${{ env.OPENWRT_VERSION }} + EOF + + echo "βœ… Release organized" + ls -la release/ + + - name: Create global checksums + run: | + cd release + sha256sum *.tar.gz > SHA256SUMS + echo "βœ… Global checksums created" + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + name: SecuBox ${{ needs.setup.outputs.version }} + body_path: release/RELEASE_NOTES.md + files: | + release/*.tar.gz + release/SHA256SUMS + draft: false + prerelease: ${{ contains(github.ref, 'alpha') || contains(github.ref, 'beta') || contains(github.ref, 'rc') }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # ============================================ + # Build status summary + # ============================================ + summary: + needs: [setup, build] + runs-on: ubuntu-latest + if: always() + + steps: + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: packages + pattern: packages-* + continue-on-error: true + + - name: Generate build summary + run: | + echo "# πŸ“Š SecuBox Build Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Version:** ${{ needs.setup.outputs.version }}" >> $GITHUB_STEP_SUMMARY + echo "**OpenWrt:** ${{ env.OPENWRT_VERSION }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "## Build Results" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| Architecture | Status | Packages |" >> $GITHUB_STEP_SUMMARY + echo "|--------------|--------|----------|" >> $GITHUB_STEP_SUMMARY + + for arch_dir in packages/packages-*/; do + if [[ -d "$arch_dir" ]]; then + ARCH=$(basename "$arch_dir" | sed 's/packages-//') + PKG_COUNT=$(find "$arch_dir" -name "*.ipk" 2>/dev/null | wc -l) + if [[ $PKG_COUNT -gt 0 ]]; then + echo "| $ARCH | βœ… Success | $PKG_COUNT |" >> $GITHUB_STEP_SUMMARY + else + echo "| $ARCH | ⚠️ No packages | 0 |" >> $GITHUB_STEP_SUMMARY + fi + fi + done + + echo "" >> $GITHUB_STEP_SUMMARY + echo "## πŸ“¦ Artifacts" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Download artifacts from the Actions tab above." >> $GITHUB_STEP_SUMMARY diff --git a/build-secubox-images.yml b/build-secubox-images.yml new file mode 100644 index 00000000..a9b5c969 --- /dev/null +++ b/build-secubox-images.yml @@ -0,0 +1,398 @@ +name: Build SecuBox Images (GlobalScale) + +on: + workflow_dispatch: + inputs: + device: + description: 'Target device' + required: true + type: choice + options: + - espressobin-v7 + - espressobin-ultra + - sheeva64 + - sheeva64-wifi + - mochabin + - all + openwrt_version: + description: 'OpenWrt version' + required: true + default: '23.05.5' + type: choice + options: + - '23.05.5' + - '23.05.4' + - 'SNAPSHOT' + include_secubox: + description: 'Include SecuBox packages' + required: true + type: boolean + default: true + +env: + OPENWRT_VERSION: ${{ github.event.inputs.openwrt_version }} + +jobs: + # ============================================ + # Build firmware images for GlobalScale devices + # ============================================ + build-image: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - device: espressobin-v7 + target: mvebu + subtarget: cortexa53 + profile: globalscale_espressobin + description: "ESPRESSObin V7 (1-2GB DDR4)" + enabled: ${{ github.event.inputs.device == 'espressobin-v7' || github.event.inputs.device == 'all' }} + + - device: espressobin-ultra + target: mvebu + subtarget: cortexa53 + profile: globalscale_espressobin-ultra + description: "ESPRESSObin Ultra (PoE, WiFi)" + enabled: ${{ github.event.inputs.device == 'espressobin-ultra' || github.event.inputs.device == 'all' }} + + - device: sheeva64 + target: mvebu + subtarget: cortexa53 + profile: globalscale_sheeva64 + description: "Sheeva64 (Plug computer)" + enabled: ${{ github.event.inputs.device == 'sheeva64' || github.event.inputs.device == 'all' }} + + - device: sheeva64-wifi + target: mvebu + subtarget: cortexa53 + profile: globalscale_sheeva64 + description: "Sheeva64 WiFi (802.11ac + BT)" + enabled: ${{ github.event.inputs.device == 'sheeva64-wifi' || github.event.inputs.device == 'all' }} + + - device: mochabin + target: mvebu + subtarget: cortexa72 + profile: globalscale_mochabin + description: "MOCHAbin (Quad-core A72, 10G)" + enabled: ${{ github.event.inputs.device == 'mochabin' || github.event.inputs.device == 'all' }} + + name: ${{ matrix.description }} + if: ${{ matrix.enabled != 'false' }} + + steps: + - name: Checkout SecuBox packages + uses: actions/checkout@v4 + + - name: Free disk space + run: | + echo "🧹 Cleaning up disk space..." + sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc + sudo docker image prune --all --force + df -h + + - 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-distutils python3-setuptools rsync \ + swig unzip zlib1g-dev file wget curl qemu-utils + + - name: Clone OpenWrt + run: | + if [[ "${{ env.OPENWRT_VERSION }}" == "SNAPSHOT" ]]; then + git clone --depth 1 https://github.com/openwrt/openwrt.git openwrt + else + git clone --depth 1 --branch v${{ env.OPENWRT_VERSION }} \ + https://github.com/openwrt/openwrt.git openwrt + fi + + - name: Update feeds + run: | + cd openwrt + ./scripts/feeds update -a + ./scripts/feeds install -a + + - name: Copy SecuBox packages + if: ${{ github.event.inputs.include_secubox == 'true' }} + run: | + mkdir -p openwrt/package/secubox + + for pkg in luci-app-*/; do + if [[ -d "$pkg" ]]; then + echo "πŸ“¦ Adding $pkg" + cp -r "$pkg" openwrt/package/secubox/ + fi + done + + - name: Generate SecuBox config + run: | + cd openwrt + + # Base configuration + cat > .config << EOF + # Target + CONFIG_TARGET_${{ matrix.target }}=y + CONFIG_TARGET_${{ matrix.target }}_${{ matrix.subtarget }}=y + CONFIG_TARGET_${{ matrix.target }}_${{ matrix.subtarget }}_DEVICE_${{ matrix.profile }}=y + + # Image settings + CONFIG_TARGET_ROOTFS_SQUASHFS=y + CONFIG_TARGET_ROOTFS_EXT4FS=y + CONFIG_TARGET_KERNEL_PARTSIZE=32 + CONFIG_TARGET_ROOTFS_PARTSIZE=512 + + # Base packages + CONFIG_PACKAGE_luci=y + CONFIG_PACKAGE_luci-ssl=y + CONFIG_PACKAGE_luci-app-opkg=y + CONFIG_PACKAGE_luci-theme-openwrt-2020=y + + # Networking essentials + CONFIG_PACKAGE_curl=y + CONFIG_PACKAGE_wget-ssl=y + CONFIG_PACKAGE_iptables=y + CONFIG_PACKAGE_ip6tables=y + CONFIG_PACKAGE_kmod-nft-core=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 + CONFIG_PACKAGE_block-mount=y + + # Wireless (if applicable) + CONFIG_PACKAGE_hostapd-common=y + CONFIG_PACKAGE_wpad-basic-mbedtls=y + + # Monitoring tools + CONFIG_PACKAGE_htop=y + CONFIG_PACKAGE_iftop=y + CONFIG_PACKAGE_tcpdump=y + + # SSH + CONFIG_PACKAGE_openssh-sftp-server=y + EOF + + - name: Add SecuBox packages to config + if: ${{ github.event.inputs.include_secubox == 'true' }} + run: | + cd openwrt + + # CrowdSec + cat >> .config << EOF + CONFIG_PACKAGE_crowdsec=y + CONFIG_PACKAGE_crowdsec-firewall-bouncer=y + CONFIG_PACKAGE_luci-app-crowdsec-dashboard=y + EOF + + # Netdata + cat >> .config << EOF + CONFIG_PACKAGE_netdata=y + CONFIG_PACKAGE_luci-app-netdata-dashboard=y + EOF + + # Netifyd + cat >> .config << EOF + CONFIG_PACKAGE_netifyd=y + CONFIG_PACKAGE_luci-app-netifyd-dashboard=y + EOF + + # WireGuard + cat >> .config << EOF + CONFIG_PACKAGE_wireguard-tools=y + CONFIG_PACKAGE_kmod-wireguard=y + CONFIG_PACKAGE_luci-app-wireguard-dashboard=y + CONFIG_PACKAGE_qrencode=y + EOF + + # SecuBox core + cat >> .config << EOF + CONFIG_PACKAGE_luci-app-network-modes=y + CONFIG_PACKAGE_luci-app-client-guardian=y + CONFIG_PACKAGE_luci-app-system-hub=y + EOF + + - name: Add device-specific packages + run: | + cd openwrt + + case "${{ matrix.device }}" in + mochabin) + # 10G networking, more RAM + cat >> .config << EOF + CONFIG_PACKAGE_kmod-sfp=y + CONFIG_PACKAGE_kmod-phy-marvell-10g=y + CONFIG_PACKAGE_prometheus-node-exporter-lua=y + EOF + ;; + + espressobin-ultra|sheeva64-wifi) + # WiFi support + cat >> .config << EOF + CONFIG_PACKAGE_kmod-mt76=y + CONFIG_PACKAGE_kmod-mac80211=y + EOF + ;; + + sheeva64*) + # Minimal for plug computer + cat >> .config << EOF + # Optimized for plug form factor + CONFIG_PACKAGE_kmod-ledtrig-heartbeat=y + EOF + ;; + esac + + - name: Make defconfig + run: | + cd openwrt + make defconfig + + - name: Download packages + run: | + cd openwrt + make download -j$(nproc) V=s || make download -j1 V=s + + - name: Build firmware + run: | + cd openwrt + + echo "πŸ”¨ Building firmware for ${{ matrix.description }}..." + echo "⏱️ This may take 1-2 hours..." + + make -j$(nproc) V=s 2>&1 | tee build.log || { + echo "❌ Build failed, retrying with single thread..." + make -j1 V=s 2>&1 | tee build-retry.log + } + + - name: Prepare artifacts + run: | + mkdir -p artifacts + + # Copy firmware images + find openwrt/bin/targets -name "*.img.gz" -exec cp {} artifacts/ \; + find openwrt/bin/targets -name "*.bin" -exec cp {} artifacts/ \; + find openwrt/bin/targets -name "*sysupgrade*" -exec cp {} artifacts/ \; + find openwrt/bin/targets -name "*factory*" -exec cp {} artifacts/ \; + + # Copy packages + mkdir -p artifacts/packages + find openwrt/bin/packages -name "luci-app-*secubox*.ipk" -exec cp {} artifacts/packages/ \; 2>/dev/null || true + find openwrt/bin/packages -name "luci-app-*dashboard*.ipk" -exec cp {} artifacts/packages/ \; 2>/dev/null || true + + # Generate checksums + cd artifacts + sha256sum * > SHA256SUMS 2>/dev/null || true + + # Create info file + cat > BUILD_INFO.txt << EOF + SecuBox Firmware Build + ======================= + Device: ${{ matrix.description }} + Profile: ${{ matrix.profile }} + Target: ${{ matrix.target }}/${{ matrix.subtarget }} + OpenWrt: ${{ env.OPENWRT_VERSION }} + SecuBox: ${{ github.event.inputs.include_secubox }} + Built: $(date -u +%Y-%m-%dT%H:%M:%SZ) + Commit: ${{ github.sha }} + EOF + + echo "πŸ“¦ Artifacts:" + ls -la + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: secubox-${{ matrix.device }}-${{ env.OPENWRT_VERSION }} + path: artifacts/ + retention-days: 30 + + # ============================================ + # Create combined release for all devices + # ============================================ + release: + needs: build-image + runs-on: ubuntu-latest + if: github.event.inputs.device == 'all' + + steps: + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: firmware + pattern: secubox-* + + - name: Organize release + run: | + mkdir -p release + + for device_dir in firmware/secubox-*/; do + DEVICE=$(basename "$device_dir" | sed 's/secubox-//' | sed "s/-${{ env.OPENWRT_VERSION }}//") + echo "πŸ“¦ Processing $DEVICE..." + + # Create device archive + tar -czf "release/secubox-firmware-${DEVICE}.tar.gz" -C "$device_dir" . + done + + # Global checksums + cd release + sha256sum *.tar.gz > SHA256SUMS + + # Release notes + cat > RELEASE_NOTES.md << 'EOF' + # SecuBox Firmware Images + + Pre-built firmware images for GlobalScale devices with SecuBox modules pre-installed. + + ## Included Devices + + | Device | SoC | RAM | Description | + |--------|-----|-----|-------------| + | ESPRESSObin V7 | Armada 3720 | 1-2GB | Entry-level | + | ESPRESSObin Ultra | Armada 3720 | 1-2GB | WiFi + PoE | + | Sheeva64 | Armada 3720 | 1GB | Plug computer | + | MOCHAbin | Armada 7040 | 4-8GB | Quad-core + 10G | + + ## Pre-installed SecuBox Modules + + - luci-app-crowdsec-dashboard + - luci-app-netdata-dashboard + - luci-app-netifyd-dashboard + - luci-app-wireguard-dashboard + - luci-app-network-modes + - luci-app-client-guardian + - luci-app-system-hub + + ## Installation + + 1. Download the appropriate firmware for your device + 2. Flash using OpenWrt sysupgrade or manufacturer tools + 3. Access LuCI at http://192.168.1.1 + 4. Navigate to Services β†’ SecuBox + + ## Support + + - [Documentation](https://cybermind.fr/docs/secubox) + - [CyberMind.fr](https://cybermind.fr) + EOF + + - name: Create release + if: github.ref == 'refs/heads/main' + uses: softprops/action-gh-release@v2 + with: + name: "SecuBox Firmware ${{ env.OPENWRT_VERSION }}" + tag_name: "firmware-${{ env.OPENWRT_VERSION }}-${{ github.run_number }}" + body_path: release/RELEASE_NOTES.md + files: | + release/*.tar.gz + release/SHA256SUMS + draft: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/luci-app-client-guardian b/luci-app-client-guardian new file mode 160000 index 00000000..ad209f39 --- /dev/null +++ b/luci-app-client-guardian @@ -0,0 +1 @@ +Subproject commit ad209f3982d9722f5d9e9da90e4489176633a65c diff --git a/luci-app-crowdsec-dashboard b/luci-app-crowdsec-dashboard new file mode 160000 index 00000000..f5920202 --- /dev/null +++ b/luci-app-crowdsec-dashboard @@ -0,0 +1 @@ +Subproject commit f5920202d2b1c0652147c61e6b5c0a58c687c4ca diff --git a/luci-app-netdata-dashboard b/luci-app-netdata-dashboard new file mode 160000 index 00000000..6dca2579 --- /dev/null +++ b/luci-app-netdata-dashboard @@ -0,0 +1 @@ +Subproject commit 6dca2579d3ded391ee5158ccd6d31140239c4f6d diff --git a/luci-app-netifyd-dashboard b/luci-app-netifyd-dashboard new file mode 160000 index 00000000..944342f7 --- /dev/null +++ b/luci-app-netifyd-dashboard @@ -0,0 +1 @@ +Subproject commit 944342f7cf10a36ffd57f51f898f7a197e9167f2 diff --git a/luci-app-network-modes b/luci-app-network-modes new file mode 160000 index 00000000..f909422b --- /dev/null +++ b/luci-app-network-modes @@ -0,0 +1 @@ +Subproject commit f909422b1888a7b52fa7ed3a0d3a5a73010beef0 diff --git a/luci-app-system-hub b/luci-app-system-hub new file mode 160000 index 00000000..7ddc6498 --- /dev/null +++ b/luci-app-system-hub @@ -0,0 +1 @@ +Subproject commit 7ddc649872109f61e3e2bffc6900d11e3721bd2d diff --git a/luci-app-wireguard-dashboard b/luci-app-wireguard-dashboard new file mode 160000 index 00000000..4f9120c0 --- /dev/null +++ b/luci-app-wireguard-dashboard @@ -0,0 +1 @@ +Subproject commit 4f9120c0566bd0c649d3f7b607c6f91e97e5a0ba diff --git a/templates/Makefile.template b/templates/Makefile.template new file mode 100644 index 00000000..db5af3f5 --- /dev/null +++ b/templates/Makefile.template @@ -0,0 +1,128 @@ +# SecuBox LuCI Application Makefile Template +# Copyright (C) 2025 CyberMind.fr +# SPDX-License-Identifier: Apache-2.0 + +include $(TOPDIR)/rules.mk + +# ============================================ +# Package Information +# ============================================ +PKG_NAME:=luci-app-TEMPLATE-dashboard +PKG_VERSION:=1.0.0 +PKG_RELEASE:=1 + +PKG_LICENSE:=Apache-2.0 +PKG_MAINTAINER:=Gandalf + +# ============================================ +# LuCI Configuration +# ============================================ +LUCI_TITLE:=LuCI TEMPLATE Dashboard +LUCI_DESCRIPTION:=Dashboard for TEMPLATE on OpenWrt +LUCI_DEPENDS:=+luci-base +TEMPLATE +LUCI_PKGARCH:=all + +# Optional: Extra dependencies +# +luci-lib-jsonc +rpcd +uhttpd + +include $(TOPDIR)/feeds/luci/luci.mk + +# ============================================ +# Package Definition +# ============================================ +define Package/$(PKG_NAME) + SECTION:=luci + CATEGORY:=LuCI + SUBMENU:=3. Applications + TITLE:=$(LUCI_TITLE) + DEPENDS:=$(LUCI_DEPENDS) + PKGARCH:=$(LUCI_PKGARCH) +endef + +define Package/$(PKG_NAME)/description +$(LUCI_DESCRIPTION) + +Features: +- Real-time status monitoring +- Configuration management +- Interactive dashboard +- System integration +endef + +# ============================================ +# Installation +# ============================================ +define Package/$(PKG_NAME)/install + # JavaScript views + $(INSTALL_DIR) $(1)/www/luci-static/resources/view/TEMPLATE + $(INSTALL_DATA) ./htdocs/luci-static/resources/view/TEMPLATE/*.js \ + $(1)/www/luci-static/resources/view/TEMPLATE/ + + # API and CSS + $(INSTALL_DIR) $(1)/www/luci-static/resources/TEMPLATE + $(INSTALL_DATA) ./htdocs/luci-static/resources/TEMPLATE/*.js \ + $(1)/www/luci-static/resources/TEMPLATE/ + $(INSTALL_DATA) ./htdocs/luci-static/resources/TEMPLATE/*.css \ + $(1)/www/luci-static/resources/TEMPLATE/ + + # Menu configuration + $(INSTALL_DIR) $(1)/usr/share/luci/menu.d + $(INSTALL_DATA) ./root/usr/share/luci/menu.d/$(PKG_NAME).json \ + $(1)/usr/share/luci/menu.d/ + + # ACL configuration + $(INSTALL_DIR) $(1)/usr/share/rpcd/acl.d + $(INSTALL_DATA) ./root/usr/share/rpcd/acl.d/$(PKG_NAME).json \ + $(1)/usr/share/rpcd/acl.d/ + + # RPCD backend + $(INSTALL_DIR) $(1)/usr/libexec/rpcd + $(INSTALL_BIN) ./root/usr/libexec/rpcd/TEMPLATE \ + $(1)/usr/libexec/rpcd/ + + # UCI default config (optional) + $(INSTALL_DIR) $(1)/etc/config + $(INSTALL_CONF) ./root/etc/config/TEMPLATE \ + $(1)/etc/config/ + + # UCI defaults (optional - runs on first install) + # $(INSTALL_DIR) $(1)/etc/uci-defaults + # $(INSTALL_BIN) ./root/etc/uci-defaults/$(PKG_NAME) \ + # $(1)/etc/uci-defaults/ +endef + +# ============================================ +# Post-installation +# ============================================ +define Package/$(PKG_NAME)/postinst +#!/bin/sh +[ -n "$${IPKG_INSTROOT}" ] || { + # Reload rpcd to register new methods + /etc/init.d/rpcd reload 2>/dev/null || true + + # Clear LuCI cache + rm -rf /tmp/luci-modulecache 2>/dev/null || true + rm -rf /tmp/luci-indexcache* 2>/dev/null || true + + echo "$(PKG_NAME) installed successfully" +} +exit 0 +endef + +define Package/$(PKG_NAME)/postrm +#!/bin/sh +[ -n "$${IPKG_INSTROOT}" ] || { + # Reload rpcd + /etc/init.d/rpcd reload 2>/dev/null || true + + # Clear LuCI cache + rm -rf /tmp/luci-modulecache 2>/dev/null || true + rm -rf /tmp/luci-indexcache* 2>/dev/null || true +} +exit 0 +endef + +# ============================================ +# Build +# ============================================ +$(eval $(call BuildPackage,$(PKG_NAME))) diff --git a/test-validate.yml b/test-validate.yml new file mode 100644 index 00000000..6a923ee3 --- /dev/null +++ b/test-validate.yml @@ -0,0 +1,327 @@ +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 */Makefile; do + if [[ -f "$makefile" ]]; then + PKG=$(dirname "$makefile") + echo " πŸ” Checking $PKG..." + + # Required fields + REQUIRED_FIELDS=( + "PKG_NAME" + "PKG_VERSION" + "PKG_RELEASE" + "PKG_LICENSE" + "LUCI_TITLE" + ) + + 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 + + # Find all JSON files + 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..." + + # Install node for syntax check + 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) + + 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..." + + ERRORS=0 + + # Check RPCD scripts + while IFS= read -r script; do + echo " πŸ” Checking $script..." + if ! shellcheck -s sh "$script"; then + ERRORS=$((ERRORS + 1)) + fi + done < <(find . -path "*/rpcd/*" -type f -executable) + + # Check init scripts + while IFS= read -r script; do + echo " πŸ” Checking $script..." + if ! shellcheck -s sh "$script"; then + ERRORS=$((ERRORS + 1)) + fi + done < <(find . -path "*/init.d/*" -type f) + + if [[ $ERRORS -gt 0 ]]; then + echo "⚠️ Found $ERRORS 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" + 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" + ERRORS=$((ERRORS + 1)) + fi + done < <(find . -path "*/etc/init.d/*" -type f 2>/dev/null) + + if [[ $ERRORS -gt 0 ]]; then + echo "❌ Found $ERRORS permission errors" + exit 1 + fi + echo "βœ… File permissions correct" + + - name: Validate package structure + run: | + echo "πŸ“‹ Validating package structure..." + + for pkg in luci-app-*/; do + if [[ -d "$pkg" ]]; then + echo " πŸ“¦ Checking $pkg..." + + # Required directories/files + REQUIRED=( + "Makefile" + ) + + # Optional but recommended + RECOMMENDED=( + "htdocs/luci-static/resources" + "root/usr/share/luci/menu.d" + "root/usr/share/rpcd/acl.d" + ) + + for req in "${REQUIRED[@]}"; do + if [[ ! -e "${pkg}${req}" ]]; then + echo " ❌ Missing required: $req" + exit 1 + fi + done + + 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-distutils 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 + + - 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: | + mkdir -p ~/sdk/package/secubox + + for pkg in luci-app-*/; do + if [[ -d "$pkg" ]]; then + cp -r "$pkg" ~/sdk/package/secubox/ + fi + done + + - name: Build test + run: | + cd ~/sdk + + # Enable packages + for pkg in ~/sdk/package/secubox/*/; do + PKG_NAME=$(basename "$pkg") + echo "CONFIG_PACKAGE_${PKG_NAME}=m" >> .config + done + + make defconfig + + # Build with timeout + timeout 30m make package/secubox/compile V=s -j$(nproc) || { + echo "⚠️ Build timeout or error" + exit 1 + } + + - name: Verify output + run: | + echo "πŸ“‹ Built packages:" + find ~/sdk/bin -name "*.ipk" -exec ls -la {} \; + + PKG_COUNT=$(find ~/sdk/bin -name "*.ipk" | wc -l) + echo "πŸ“¦ Total packages built: $PKG_COUNT" + + if [[ $PKG_COUNT -eq 0 ]]; then + echo "❌ No packages were built!" + exit 1 + fi + + # ============================================ + # 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 "| 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-) + + echo "| $PKG_NAME | $PKG_VERSION | $PKG_TITLE |" >> PACKAGES.md + fi + done + + echo "" >> PACKAGES.md + echo "Generated: $(date -u +%Y-%m-%dT%H:%M:%SZ)" >> PACKAGES.md + + cat PACKAGES.md + + - name: Upload docs + uses: actions/upload-artifact@v4 + with: + name: documentation + path: PACKAGES.md