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: '24.10.5' type: choice options: - '24.10.5' - '23.05.5' build_toolchain_packages: description: 'Build toolchain packages (Go/C++)' required: false default: true type: boolean architectures: description: 'Architectures (comma-separated or "all")' required: false default: 'all' env: OPENWRT_VERSION: ${{ github.event.inputs.openwrt_version || '24.10.5' }} permissions: contents: write jobs: # ============================================ # Setup - determine version and build matrix # Mirrors: local-build.sh setup logic # ============================================ setup: runs-on: ubuntu-latest outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} version: ${{ steps.version.outputs.version }} sdk_packages: ${{ steps.packages.outputs.sdk_packages }} toolchain_packages: ${{ steps.packages.outputs.toolchain_packages }} 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: Categorize packages (mirrors local-build.sh OPENWRT_ONLY_PACKAGES) id: packages run: | # Toolchain-only packages (from local-build.sh OPENWRT_ONLY_PACKAGES) # These compile native code and need full OpenWrt buildroot TOOLCHAIN_ONLY=( # C/C++ native binaries "secubox-app-netifyd" "secubox-app-ndpid" "secubox-app-nodogsplash" # Go binaries "secubox-app-crowdsec" "secubox-app-cs-firewall-bouncer" # Python/special packages "secubox-app-mitmproxy" "secubox-app-metablogizer" "secubox-app-tor" ) SDK_PACKAGES="" TOOLCHAIN_PACKAGES="" # Find all secubox packages for pkg_dir in package/secubox/*/; do [ -f "${pkg_dir}Makefile" ] || continue PKG_NAME=$(basename "$pkg_dir") # Check if it's in toolchain-only list IS_TOOLCHAIN=false for tc_pkg in "${TOOLCHAIN_ONLY[@]}"; do if [[ "$PKG_NAME" == "$tc_pkg" ]]; then IS_TOOLCHAIN=true break fi done if $IS_TOOLCHAIN; then TOOLCHAIN_PACKAGES="${TOOLCHAIN_PACKAGES}${PKG_NAME}," else SDK_PACKAGES="${SDK_PACKAGES}${PKG_NAME}," fi done # Root level luci-app-* (all SDK buildable) for pkg_dir in luci-app-*/; do [ -f "${pkg_dir}Makefile" ] || continue PKG_NAME=$(basename "$pkg_dir") SDK_PACKAGES="${SDK_PACKAGES}${PKG_NAME}," done # luci-theme-secubox [ -d "luci-theme-secubox" ] && SDK_PACKAGES="${SDK_PACKAGES}luci-theme-secubox," echo "sdk_packages=${SDK_PACKAGES%,}" >> $GITHUB_OUTPUT echo "toolchain_packages=${TOOLCHAIN_PACKAGES%,}" >> $GITHUB_OUTPUT echo "📦 SDK packages (PKGARCH:=all): ${SDK_PACKAGES%,}" echo "🔧 Toolchain packages (native binaries): ${TOOLCHAIN_PACKAGES%,}" - name: Set build matrix (mirrors local-build.sh set_architecture) id: set-matrix run: | # Architecture mapping from local-build.sh cat > /tmp/matrix.json << 'EOF' { "include": [ { "target": "x86-64", "arch": "x86_64", "sdk_path": "x86/64", "toolchain_target": "x86", "toolchain_subtarget": "64", "description": "x86 64-bit (PC, VM)" }, { "target": "aarch64-cortex-a72", "arch": "aarch64_cortex-a72", "sdk_path": "mvebu/cortexa72", "toolchain_target": "mvebu", "toolchain_subtarget": "cortexa72", "description": "ARM Cortex-A72 (MOCHAbin, RPi4)" }, { "target": "aarch64-cortex-a53", "arch": "aarch64_cortex-a53", "sdk_path": "mvebu/cortexa53", "toolchain_target": "mvebu", "toolchain_subtarget": "cortexa53", "description": "ARM Cortex-A53 (ESPRESSObin)" }, { "target": "aarch64-generic", "arch": "aarch64_generic", "sdk_path": "armsr/armv8", "toolchain_target": "armsr", "toolchain_subtarget": "armv8", "description": "ARM 64-bit generic" }, { "target": "rockchip-armv8", "arch": "aarch64_generic", "sdk_path": "rockchip/armv8", "toolchain_target": "rockchip", "toolchain_subtarget": "armv8", "description": "Rockchip (NanoPi R4S/R5S)" }, { "target": "mips-24kc", "arch": "mips_24kc", "sdk_path": "ath79/generic", "toolchain_target": "ath79", "toolchain_subtarget": "generic", "description": "MIPS 24Kc (Atheros/QCA)" }, { "target": "mipsel-24kc", "arch": "mipsel_24kc", "sdk_path": "ramips/mt7621", "toolchain_target": "ramips", "toolchain_subtarget": "mt7621", "description": "MIPS LE (MT7621)" } ] } EOF # Filter by input architectures if specified INPUT_ARCHS="${{ github.event.inputs.architectures }}" if [[ -z "$INPUT_ARCHS" || "$INPUT_ARCHS" == "all" ]]; then MATRIX=$(cat /tmp/matrix.json) else MATRIX=$(jq -c --arg archs "$INPUT_ARCHS" ' .include |= map(select(.target as $t | $archs | split(",") | map(gsub("^\\s+|\\s+$";"")) | any(. == $t))) ' /tmp/matrix.json) fi echo "matrix=$(echo "$MATRIX" | jq -c)" >> $GITHUB_OUTPUT echo "📋 Build matrix:" echo "$MATRIX" | jq '.' # ============================================ # Build SDK packages (LuCI apps, shell scripts) # Mirrors: local-build.sh SDK build path # ============================================ build-sdk: needs: setup runs-on: ubuntu-latest strategy: fail-fast: false matrix: ${{ fromJson(needs.setup.outputs.matrix) }} name: SDK ${{ matrix.target }} steps: - name: Checkout source uses: actions/checkout@v4 - name: Free disk space run: | sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /opt/hostedtoolcache/CodeQL sudo docker image prune --all --force - name: Install dependencies run: | sudo apt-get update sudo apt-get install -y build-essential clang flex bison g++ gawk \ gcc-multilib g++-multilib gettext git libncurses5-dev libssl-dev \ python3-setuptools rsync 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.target }}-v6 - name: Download OpenWrt SDK if: steps.cache-sdk.outputs.cache-hit != 'true' run: | BASE_URL="https://downloads.openwrt.org/releases/${{ env.OPENWRT_VERSION }}/targets/${{ matrix.sdk_path }}" for attempt in 1 2 3; do SDK_FILE=$(curl -sL "$BASE_URL/" | grep -oP 'openwrt-sdk[^"<>]+\.tar\.(xz|zst)' | head -1) && break sleep 10 done [ -z "$SDK_FILE" ] && { echo "❌ Could not find SDK"; exit 1; } echo "📥 Downloading: $SDK_FILE" wget -q "${BASE_URL}/${SDK_FILE}" -O /tmp/sdk.tar.xz mkdir -p sdk tar -xf /tmp/sdk.tar.xz -C sdk --strip-components=1 rm -f /tmp/sdk.tar.xz - name: Setup SDK feeds (mirrors local-build.sh) run: | cd sdk # Remove unwanted feeds (from local-build.sh) sed -i '/telephony/d; /routing/d' feeds.conf.default 2>/dev/null || true # Determine branch based on OpenWrt version VERSION="${{ env.OPENWRT_VERSION }}" if [[ "$VERSION" =~ ^24\. ]]; then BRANCH="openwrt-24.10" elif [[ "$VERSION" =~ ^23\. ]]; then BRANCH="openwrt-23.05" else BRANCH="master" fi echo "📌 Using branch: $BRANCH for OpenWrt $VERSION" cat > feeds.conf << EOF src-git packages https://github.com/openwrt/packages.git;$BRANCH src-git luci https://github.com/openwrt/luci.git;$BRANCH src-link secubox ${{ github.workspace }}/secubox-tools/local-feed EOF # Update feeds with retry for feed in packages luci; do for attempt in 1 2 3; do ./scripts/feeds update $feed && break sleep $((10 * attempt)) done done ./scripts/feeds install -a make defconfig - name: Copy packages to SDK run: | VERSION="${{ needs.setup.outputs.version }}" # Sync to local-feed first (mirrors local-build.sh) mkdir -p secubox-tools/local-feed for pkg in package/secubox/*/; do [ -f "${pkg}Makefile" ] || continue PKG_NAME=$(basename "$pkg") cp -r "$pkg" "secubox-tools/local-feed/" # Update version in copied package sed -i "s/PKG_VERSION:=.*/PKG_VERSION:=$VERSION/" "secubox-tools/local-feed/${PKG_NAME}/Makefile" 2>/dev/null || true sed -i "s/PKG_RELEASE:=.*/PKG_RELEASE:=1/" "secubox-tools/local-feed/${PKG_NAME}/Makefile" 2>/dev/null || true done # Copy root level luci-app-* for pkg in luci-app-*/; do [ -f "${pkg}Makefile" ] || continue cp -r "$pkg" "secubox-tools/local-feed/" done # Copy theme [ -d "luci-theme-secubox" ] && cp -r "luci-theme-secubox" "secubox-tools/local-feed/" # Update secubox feed cd sdk ./scripts/feeds update secubox ./scripts/feeds install -a -p secubox - name: Build SDK packages run: | cd sdk SDK_PACKAGES="${{ needs.setup.outputs.sdk_packages }}" echo "📦 Building SDK packages: $SDK_PACKAGES" # Enable packages for pkg in ${SDK_PACKAGES//,/ }; do echo "CONFIG_PACKAGE_${pkg}=m" >> .config # Remove LUCI_DEPENDS for SDK build (runtime only) PKG_DIR=$(find package -name "$pkg" -type d | head -1) [ -n "$PKG_DIR" ] && sed -i 's/^LUCI_DEPENDS:=/#&/' "$PKG_DIR/Makefile" 2>/dev/null || true done make defconfig # Build each package BUILT=0 FAILED=0 BUILT_LIST="" FAILED_LIST="" for pkg in ${SDK_PACKAGES//,/ }; do echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "📦 Building: $pkg" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" if timeout 300 make package/$pkg/compile V=s -j$(nproc) 2>&1 | tail -30; then PKG_FILE=$(find bin -name "${pkg}*.ipk" 2>/dev/null | head -1) if [ -n "$PKG_FILE" ]; then echo "✅ Built: $pkg → $PKG_FILE" BUILT=$((BUILT + 1)) BUILT_LIST="${BUILT_LIST}${pkg}," else echo "⚠️ No .ipk generated for $pkg" FAILED=$((FAILED + 1)) FAILED_LIST="${FAILED_LIST}${pkg}," fi else echo "❌ Build failed: $pkg" FAILED=$((FAILED + 1)) FAILED_LIST="${FAILED_LIST}${pkg}," fi done echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "📊 SDK Build Summary for ${{ matrix.target }}" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "✅ Built: $BUILT packages" echo "❌ Failed: $FAILED packages" [ -n "$BUILT_LIST" ] && echo "Built: ${BUILT_LIST%,}" [ -n "$FAILED_LIST" ] && echo "Failed: ${FAILED_LIST%,}" - name: Collect SDK artifacts id: collect run: | mkdir -p artifacts/${{ matrix.target }} find sdk/bin -name "*.ipk" -exec cp {} artifacts/${{ matrix.target }}/ \; 2>/dev/null || true PKG_COUNT=$(find artifacts/${{ matrix.target }} -name "*.ipk" 2>/dev/null | wc -l) echo "pkg_count=$PKG_COUNT" >> $GITHUB_OUTPUT echo "📦 SDK packages for ${{ matrix.target }}: $PKG_COUNT" ls -la artifacts/${{ matrix.target }}/ 2>/dev/null || echo "None" # Create checksums [ "$PKG_COUNT" -gt 0 ] && (cd artifacts/${{ matrix.target }} && sha256sum *.ipk > SHA256SUMS) - name: Upload SDK artifacts uses: actions/upload-artifact@v4 if: steps.collect.outputs.pkg_count > 0 with: name: sdk-packages-${{ matrix.target }} path: artifacts/${{ matrix.target }}/ retention-days: 30 # ============================================ # Build Toolchain packages (Go, C/C++) # Mirrors: local-build.sh OpenWrt buildroot path # ============================================ build-toolchain: needs: setup if: github.event.inputs.build_toolchain_packages != 'false' && needs.setup.outputs.toolchain_packages != '' runs-on: ubuntu-latest strategy: fail-fast: false matrix: ${{ fromJson(needs.setup.outputs.matrix) }} name: Toolchain ${{ matrix.target }} steps: - name: Checkout source uses: actions/checkout@v4 - name: Free disk space run: | sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /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-setuptools python3-dev rsync swig unzip zlib1g-dev file wget - name: Cache OpenWrt Toolchain uses: actions/cache@v4 id: cache-toolchain with: path: openwrt key: openwrt-toolchain-${{ env.OPENWRT_VERSION }}-${{ matrix.target }}-v4 - name: Clone OpenWrt if: steps.cache-toolchain.outputs.cache-hit != 'true' run: | VERSION="${{ env.OPENWRT_VERSION }}" if [[ "$VERSION" =~ ^24\. ]]; then BRANCH="openwrt-24.10" elif [[ "$VERSION" =~ ^23\. ]]; then BRANCH="openwrt-23.05" else BRANCH="master" fi git clone --depth 1 -b "$BRANCH" https://github.com/openwrt/openwrt.git - name: Setup OpenWrt feeds run: | cd openwrt ./scripts/feeds update -a ./scripts/feeds install -a - name: Configure and build toolchain if: steps.cache-toolchain.outputs.cache-hit != 'true' run: | cd openwrt # Set target configuration cat > .config << EOF CONFIG_TARGET_${{ matrix.toolchain_target }}=y CONFIG_TARGET_${{ matrix.toolchain_target }}_${{ matrix.toolchain_subtarget }}=y CONFIG_PACKAGE_golang=y CONFIG_PACKAGE_golang-src=y EOF make defconfig echo "⚙️ Building toolchain for ${{ matrix.target }}..." make toolchain/install -j$(nproc) V=s || make toolchain/install -j1 V=s - name: Copy toolchain packages run: | VERSION="${{ needs.setup.outputs.version }}" TOOLCHAIN_PACKAGES="${{ needs.setup.outputs.toolchain_packages }}" mkdir -p openwrt/package/secubox for pkg in ${TOOLCHAIN_PACKAGES//,/ }; do if [ -d "package/secubox/$pkg" ]; then cp -r "package/secubox/$pkg" "openwrt/package/secubox/" sed -i "s/PKG_VERSION:=.*/PKG_VERSION:=$VERSION/" "openwrt/package/secubox/${pkg}/Makefile" 2>/dev/null || true sed -i "s/PKG_RELEASE:=.*/PKG_RELEASE:=1/" "openwrt/package/secubox/${pkg}/Makefile" 2>/dev/null || true fi done - name: Build toolchain packages run: | cd openwrt TOOLCHAIN_PACKAGES="${{ needs.setup.outputs.toolchain_packages }}" echo "🔧 Building toolchain packages: $TOOLCHAIN_PACKAGES" for pkg in ${TOOLCHAIN_PACKAGES//,/ }; do echo "CONFIG_PACKAGE_${pkg}=m" >> .config done make defconfig BUILT=0 FAILED=0 for pkg in ${TOOLCHAIN_PACKAGES//,/ }; do echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "🔧 Building: $pkg" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" if timeout 1800 make package/$pkg/compile V=s -j$(nproc) 2>&1 | tail -50; then PKG_FILE=$(find bin -name "${pkg}*.ipk" 2>/dev/null | head -1) if [ -n "$PKG_FILE" ]; then echo "✅ Built: $pkg → $PKG_FILE" BUILT=$((BUILT + 1)) else echo "⚠️ No .ipk generated for $pkg" FAILED=$((FAILED + 1)) fi else echo "❌ Build failed: $pkg" FAILED=$((FAILED + 1)) fi done echo "" echo "📊 Toolchain Build Summary: $BUILT built, $FAILED failed" - name: Collect toolchain artifacts id: collect run: | mkdir -p artifacts/${{ matrix.target }} find openwrt/bin -name "*.ipk" -exec cp {} artifacts/${{ matrix.target }}/ \; 2>/dev/null || true PKG_COUNT=$(find artifacts/${{ matrix.target }} -name "*.ipk" 2>/dev/null | wc -l) echo "pkg_count=$PKG_COUNT" >> $GITHUB_OUTPUT echo "🔧 Toolchain packages for ${{ matrix.target }}: $PKG_COUNT" ls -la artifacts/${{ matrix.target }}/ 2>/dev/null || echo "None" - name: Upload toolchain artifacts uses: actions/upload-artifact@v4 if: steps.collect.outputs.pkg_count > 0 with: name: toolchain-packages-${{ matrix.target }} path: artifacts/${{ matrix.target }}/ retention-days: 30 # ============================================ # Combine and publish artifacts # ============================================ publish: needs: [setup, build-sdk, build-toolchain] if: always() && (needs.build-sdk.result == 'success' || needs.build-toolchain.result == 'success') runs-on: ubuntu-latest steps: - name: Download all artifacts uses: actions/download-artifact@v4 with: path: packages pattern: '*-packages-*' - name: Combine packages per architecture run: | VERSION="${{ needs.setup.outputs.version }}" mkdir -p release # Combine SDK and toolchain packages per architecture for dir in packages/*/; do [ -d "$dir" ] || continue # Extract architecture from directory name ARCH=$(basename "$dir" | sed 's/.*-packages-//') mkdir -p "release/${ARCH}" # Copy all .ipk files find "$dir" -name "*.ipk" -exec cp {} "release/${ARCH}/" \; 2>/dev/null || true done # Create per-architecture tarballs for arch_dir in release/*/; do [ -d "$arch_dir" ] || continue ARCH=$(basename "$arch_dir") PKG_COUNT=$(find "$arch_dir" -name "*.ipk" | wc -l) if [ "$PKG_COUNT" -gt 0 ]; then tar -czf "release/secubox-${VERSION}-${ARCH}.tar.gz" -C "release/${ARCH}" . echo "📦 $ARCH: $PKG_COUNT packages" fi done # Create all-in-one archive tar -czf "release/secubox-${VERSION}-all-architectures.tar.gz" -C release . # Checksums cd release sha256sum *.tar.gz > SHA256SUMS 2>/dev/null || true echo "" echo "📋 Release contents:" ls -la - name: Upload combined release uses: actions/upload-artifact@v4 with: name: secubox-release-${{ needs.setup.outputs.version }} path: | release/*.tar.gz release/SHA256SUMS retention-days: 90 # ============================================ # Create GitHub Release (on tags) # ============================================ release: needs: [setup, publish] if: startsWith(github.ref, 'refs/tags/v') runs-on: ubuntu-latest steps: - name: Download release artifact uses: actions/download-artifact@v4 with: name: secubox-release-${{ needs.setup.outputs.version }} path: release - name: Create GitHub Release uses: softprops/action-gh-release@v2 with: name: SecuBox v${{ needs.setup.outputs.version }} body: | ## 📦 SecuBox Packages v${{ needs.setup.outputs.version }} Pre-built OpenWrt packages for OpenWrt ${{ env.OPENWRT_VERSION }}. ### 📥 Installation ```bash # Upload .ipk files to router opkg update opkg install /tmp/*.ipk /etc/init.d/rpcd restart ``` ### 🏗️ Supported Architectures | Target | Description | |--------|-------------| | `x86-64` | PC, VMs, Proxmox | | `aarch64-cortex-a72` | MOCHAbin, Raspberry Pi 4 | | `aarch64-cortex-a53` | ESPRESSObin | | `aarch64-generic` | NanoPi R4S/R5S, Generic ARM64 | | `rockchip-armv8` | NanoPi R4S/R5S (Rockchip) | | `mips-24kc` | TP-Link, Atheros/QCA | | `mipsel-24kc` | Xiaomi, GL.iNet, MT7621 | ### 📦 Package Types - **SDK packages**: LuCI apps, shell scripts (PKGARCH:=all) - **Toolchain packages**: Go binaries (crowdsec), C++ binaries (netifyd) files: | release/*.tar.gz release/SHA256SUMS draft: false prerelease: false env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # ============================================ # Summary # ============================================ summary: needs: [setup, build-sdk, build-toolchain, publish] if: always() runs-on: ubuntu-latest steps: - name: Download artifacts for summary uses: actions/download-artifact@v4 with: path: packages pattern: '*-packages-*' continue-on-error: true - name: Generate summary run: | echo "# 📊 SecuBox Build Summary" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "| Property | Value |" >> $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 "## Package Categories" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "**SDK Packages** (PKGARCH:=all):" >> $GITHUB_STEP_SUMMARY echo '```' >> $GITHUB_STEP_SUMMARY echo "${{ needs.setup.outputs.sdk_packages }}" | tr ',' '\n' >> $GITHUB_STEP_SUMMARY echo '```' >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "**Toolchain Packages** (native binaries):" >> $GITHUB_STEP_SUMMARY echo '```' >> $GITHUB_STEP_SUMMARY echo "${{ needs.setup.outputs.toolchain_packages }}" | tr ',' '\n' >> $GITHUB_STEP_SUMMARY echo '```' >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "## Build Results" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "| Job | Status |" >> $GITHUB_STEP_SUMMARY echo "|-----|--------|" >> $GITHUB_STEP_SUMMARY echo "| SDK Build | ${{ needs.build-sdk.result }} |" >> $GITHUB_STEP_SUMMARY echo "| Toolchain Build | ${{ needs.build-toolchain.result }} |" >> $GITHUB_STEP_SUMMARY echo "| Publish | ${{ needs.publish.result }} |" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "## Packages per Architecture" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "| Architecture | SDK | Toolchain | Total |" >> $GITHUB_STEP_SUMMARY echo "|--------------|-----|-----------|-------|" >> $GITHUB_STEP_SUMMARY for dir in packages/*/; do [ -d "$dir" ] || continue ARCH=$(basename "$dir" | sed 's/.*-packages-//') TYPE=$(basename "$dir" | sed 's/-packages-.*//') COUNT=$(find "$dir" -name "*.ipk" 2>/dev/null | wc -l) if [[ "$TYPE" == "sdk" ]]; then echo "| $ARCH | $COUNT | - | $COUNT |" >> $GITHUB_STEP_SUMMARY else echo "| $ARCH | - | $COUNT | $COUNT |" >> $GITHUB_STEP_SUMMARY fi done