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