secubox-openwrt/.github/workflows/build-openwrt-packages.yml

723 lines
25 KiB
YAML

name: Build OpenWrt Packages
on:
push:
branches: [main, master, develop]
tags:
- 'v*'
pull_request:
branches: [main, master]
workflow_dispatch:
inputs:
package_name:
description: 'Package to build (leave empty for all packages)'
required: false
type: choice
options:
- ''
- 'luci-app-secubox'
- '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'
- 'luci-app-bandwidth-manager'
- 'luci-app-auth-guardian'
- 'luci-app-media-flow'
- 'luci-app-vhost-manager'
- 'luci-app-cdn-cache'
openwrt_version:
description: 'OpenWrt version'
required: true
default: '23.05.5'
type: choice
options:
- '23.05.5'
- '23.05.4'
- '24.10.0'
- 'SNAPSHOT'
architectures:
description: 'Architectures (comma-separated or "all")'
required: false
default: 'x86-64'
env:
OPENWRT_VERSION: ${{ github.event.inputs.openwrt_version || '23.05.5' }}
permissions:
contents: write
jobs:
# ============================================
# Setup and 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}"
PACKAGE_NAME="${{ github.event.inputs.package_name }}"
if [[ -n "$PACKAGE_NAME" ]]; then
echo "🎯 Building single package: $PACKAGE_NAME"
else
echo "📦 Building all packages"
fi
- name: Set build matrix
id: set-matrix
run: |
cat > /tmp/matrix.json << 'EOF'
{
"include": [
{
"target": "x86-64",
"arch": "x86_64",
"sdk_path": "x86/64",
"description": "x86 64-bit (PC, VM)"
},
{
"target": "aarch64-cortex-a53",
"arch": "aarch64_cortex-a53",
"sdk_path": "mvebu/cortexa53",
"description": "ARM Cortex-A53 (ESPRESSObin)"
},
{
"target": "aarch64-cortex-a72",
"arch": "aarch64_cortex-a72",
"sdk_path": "mvebu/cortexa72",
"description": "ARM Cortex-A72 (MOCHAbin)"
},
{
"target": "aarch64-generic",
"arch": "aarch64_generic",
"sdk_path": "armsr/armv8",
"description": "ARM 64-bit generic"
},
{
"target": "mips-24kc",
"arch": "mips_24kc",
"sdk_path": "ath79/generic",
"description": "MIPS 24Kc (TP-Link)"
},
{
"target": "mipsel-24kc",
"arch": "mipsel_24kc",
"sdk_path": "ramips/mt7621",
"description": "MIPS LE (Xiaomi, GL.iNet)"
},
{
"target": "mediatek-filogic",
"arch": "aarch64_cortex-a53",
"sdk_path": "mediatek/filogic",
"description": "MediaTek Filogic"
},
{
"target": "rockchip-armv8",
"arch": "aarch64_generic",
"sdk_path": "rockchip/armv8",
"description": "Rockchip (NanoPi R4S)"
},
{
"target": "bcm27xx-bcm2711",
"arch": "aarch64_cortex-a72",
"sdk_path": "bcm27xx/bcm2711",
"description": "Raspberry Pi 4"
}
]
}
EOF
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<<EOF" >> $GITHUB_OUTPUT
echo "$MATRIX" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
echo "📋 Build matrix:"
echo "$MATRIX" | jq '.'
# ============================================
# Build packages
# ============================================
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 /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 curl jq
- name: Cache OpenWrt SDK
uses: actions/cache@v4
id: cache-sdk
with:
path: sdk
key: openwrt-sdk-${{ env.OPENWRT_VERSION }}-${{ matrix.target }}-v4
- name: Download OpenWrt SDK
if: steps.cache-sdk.outputs.cache-hit != 'true'
run: |
echo "📥 Downloading SDK for ${{ matrix.description }}..."
BASE_URL="https://downloads.openwrt.org/releases/${{ env.OPENWRT_VERSION }}/targets/${{ matrix.sdk_path }}"
# Find SDK filename with retry
for attempt in 1 2 3; do
echo "Attempt $attempt: Fetching SDK list..."
SDK_FILE=$(curl -sL --retry 3 --retry-delay 5 "$BASE_URL/" | grep -oP 'openwrt-sdk[^"<>]+\.tar\.(xz|zst)' | head -1) && break
sleep 10
done
if [[ -z "$SDK_FILE" ]]; then
echo "❌ Could not find SDK"
exit 1
fi
echo "📥 Downloading: $SDK_FILE"
# Download with retry
for attempt in 1 2 3; do
echo "Download attempt $attempt..."
wget -q --retry-connrefused --waitretry=5 --timeout=60 \
"${BASE_URL}/${SDK_FILE}" -O /tmp/sdk.tar.xz && break
sleep 15
done
# Extract
mkdir -p sdk
tar -xf /tmp/sdk.tar.xz -C sdk --strip-components=1
rm -f /tmp/sdk.tar.xz
echo "✅ SDK extracted"
- name: Setup SDK feeds (GitHub mirrors)
run: |
cd sdk
echo "📝 Configuring feeds with GitHub mirrors..."
# Use GitHub mirrors - only essential feeds (no telephony)
cat > feeds.conf << 'FEEDS'
src-git base https://github.com/openwrt/openwrt.git;openwrt-23.05
src-git packages https://github.com/openwrt/packages.git;openwrt-23.05
src-git luci https://github.com/openwrt/luci.git;openwrt-23.05
FEEDS
echo "📋 feeds.conf:"
cat feeds.conf
# Update feeds individually with error handling
echo ""
echo "🔄 Updating feeds..."
FEEDS_OK=0
for feed in base packages luci; do
echo "Updating feed: $feed"
for attempt in 1 2 3; do
if ./scripts/feeds update $feed 2>&1; then
echo " ✅ $feed updated"
FEEDS_OK=$((FEEDS_OK + 1))
break
fi
echo " ⚠️ Attempt $attempt failed, retrying..."
sleep $((10 * attempt))
done
done
echo ""
echo "📊 Feeds updated: $FEEDS_OK/3"
# Install feeds
echo ""
echo "📦 Installing feeds..."
./scripts/feeds install -a 2>&1 || true
# Verify luci.mk exists
if [[ -f "feeds/luci/luci.mk" ]]; then
echo "✅ luci.mk found"
else
echo "⚠️ Creating fallback luci.mk..."
mkdir -p feeds/luci
cat > feeds/luci/luci.mk << 'LUCI_MK'
# Minimal LuCI build system fallback
LUCI_PKGARCH:=all
define Package/Default
SECTION:=luci
CATEGORY:=LuCI
SUBMENU:=3. Applications
PKGARCH:=all
endef
LUCI_MK
fi
# Clean up any stale feed references
rm -f feeds/telephony.index 2>/dev/null || true
rm -rf feeds/telephony 2>/dev/null || true
make defconfig
echo "✅ SDK configured"
- name: Copy packages to SDK
run: |
VERSION="${{ needs.setup.outputs.version }}"
PACKAGE_NAME="${{ github.event.inputs.package_name }}"
if [[ -n "$PACKAGE_NAME" ]]; then
echo "📦 Copying single package: $PACKAGE_NAME (version: $VERSION)..."
if [[ -d "$PACKAGE_NAME" && -f "${PACKAGE_NAME}/Makefile" ]]; then
echo " 📁 $PACKAGE_NAME"
cp -r "$PACKAGE_NAME" sdk/package/
# Update version
sed -i "s/PKG_VERSION:=.*/PKG_VERSION:=$VERSION/" "sdk/package/${PACKAGE_NAME}/Makefile"
sed -i "s/PKG_RELEASE:=.*/PKG_RELEASE:=1/" "sdk/package/${PACKAGE_NAME}/Makefile"
else
echo "❌ Package $PACKAGE_NAME not found or missing Makefile"
exit 1
fi
else
echo "📦 Copying all packages (version: $VERSION)..."
for pkg in luci-app-*/; do
if [[ -d "$pkg" && -f "${pkg}Makefile" ]]; then
PKG_NAME=$(basename "$pkg")
echo " 📁 $PKG_NAME"
cp -r "$pkg" sdk/package/
# Update version
sed -i "s/PKG_VERSION:=.*/PKG_VERSION:=$VERSION/" "sdk/package/${PKG_NAME}/Makefile"
sed -i "s/PKG_RELEASE:=.*/PKG_RELEASE:=1/" "sdk/package/${PKG_NAME}/Makefile"
fi
done
fi
echo ""
echo "📋 Packages in SDK:"
ls -d sdk/package/luci-app-*/ 2>/dev/null || echo "None"
- name: Configure packages
run: |
cd sdk
echo "⚙️ Enabling packages..."
for pkg in package/luci-app-*/; do
if [[ -d "$pkg" ]]; then
PKG_NAME=$(basename "$pkg")
echo "CONFIG_PACKAGE_${PKG_NAME}=m" >> .config
echo " ✅ $PKG_NAME"
fi
done
make defconfig
- name: Build packages
run: |
cd sdk
echo "🔨 Building SecuBox packages..."
echo ""
BUILT=0
FAILED=0
BUILT_LIST=""
FAILED_LIST=""
for pkg in package/luci-app-*/; do
[[ -d "$pkg" ]] || continue
PKG_NAME=$(basename "$pkg")
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📦 Building: $PKG_NAME"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
# Show package contents for debugging
echo "📁 Package contents:"
ls -la "$pkg"
# Verify Makefile syntax
if ! grep -q "BuildPackage" "${pkg}Makefile"; then
echo "⚠️ WARNING: Makefile missing BuildPackage call"
fi
# Build with timeout (10 minutes per package)
BUILD_LOG="/tmp/build-${PKG_NAME}.log"
if timeout 600 make package/${PKG_NAME}/compile V=s -j$(nproc) > "$BUILD_LOG" 2>&1; then
# Check if .ipk was created
IPK_FILE=$(find bin -name "${PKG_NAME}*.ipk" 2>/dev/null | head -1)
if [[ -n "$IPK_FILE" ]]; then
echo "✅ Built: $PKG_NAME"
echo " → $IPK_FILE"
BUILT=$((BUILT + 1))
BUILT_LIST="${BUILT_LIST}${PKG_NAME},"
else
echo "⚠️ No .ipk generated for $PKG_NAME"
echo "📋 Last 50 lines of build log:"
tail -50 "$BUILD_LOG"
FAILED=$((FAILED + 1))
FAILED_LIST="${FAILED_LIST}${PKG_NAME},"
fi
else
echo "❌ Build failed: $PKG_NAME"
echo "📋 Last 100 lines of build log:"
tail -100 "$BUILD_LOG"
FAILED=$((FAILED + 1))
FAILED_LIST="${FAILED_LIST}${PKG_NAME},"
fi
echo ""
done
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📊 Build Summary"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "✅ Built: $BUILT packages"
echo "❌ Failed: $FAILED packages"
echo ""
echo "Built: $BUILT_LIST"
if [[ -n "$FAILED_LIST" ]]; then
echo "Failed: $FAILED_LIST"
fi
- name: Collect artifacts
id: collect
run: |
echo "📦 Collecting artifacts..."
mkdir -p artifacts/${{ matrix.target }}
# Find and copy .ipk files
find sdk/bin -name "luci-app-*.ipk" -exec cp {} artifacts/${{ matrix.target }}/ \; 2>/dev/null || true
# Also collect any SecuBox related packages
find sdk/bin -name "*secubox*.ipk" -exec cp {} artifacts/${{ matrix.target }}/ \; 2>/dev/null || true
# Count
PKG_COUNT=$(find artifacts/${{ matrix.target }} -name "*.ipk" 2>/dev/null | wc -l)
echo "pkg_count=$PKG_COUNT" >> $GITHUB_OUTPUT
echo ""
echo "📋 Built packages for ${{ matrix.target }}:"
ls -la artifacts/${{ matrix.target }}/ 2>/dev/null || echo "No packages"
# Create checksums
if [[ $PKG_COUNT -gt 0 ]]; then
cd artifacts/${{ matrix.target }}
sha256sum *.ipk > SHA256SUMS
fi
echo ""
echo "📦 Total: $PKG_COUNT packages"
- name: Upload artifacts
uses: actions/upload-artifact@v4
if: steps.collect.outputs.pkg_count > 0
with:
name: packages-${{ matrix.target }}
path: artifacts/${{ matrix.target }}/
retention-days: 30
- name: Build Summary
run: |
PKG_COUNT="${{ steps.collect.outputs.pkg_count }}"
echo "## 📦 Build Results: ${{ matrix.target }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Property | Value |" >> $GITHUB_STEP_SUMMARY
echo "|----------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| Target | ${{ matrix.description }} |" >> $GITHUB_STEP_SUMMARY
echo "| Architecture | ${{ matrix.arch }} |" >> $GITHUB_STEP_SUMMARY
echo "| SDK Path | ${{ matrix.sdk_path }} |" >> $GITHUB_STEP_SUMMARY
echo "| Packages Built | $PKG_COUNT |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [[ "$PKG_COUNT" -gt 0 ]]; then
echo "### Built Packages" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
ls artifacts/${{ matrix.target }}/*.ipk 2>/dev/null | xargs -I{} basename {} | sort
echo '```' >> $GITHUB_STEP_SUMMARY
fi
# ============================================
# Publish combined artifacts (always)
# ============================================
publish-artifacts:
needs: [setup, build]
runs-on: ubuntu-latest
if: always() && needs.build.result == 'success'
steps:
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: packages
pattern: packages-*
- name: Create combined archives
run: |
VERSION="${{ needs.setup.outputs.version }}"
PACKAGE_NAME="${{ github.event.inputs.package_name }}"
mkdir -p release
# Determine prefix for archives
if [[ -n "$PACKAGE_NAME" ]]; then
PREFIX="${PACKAGE_NAME}"
echo "📁 Creating archives for single package: $PACKAGE_NAME..."
else
PREFIX="secubox"
echo "📁 Creating combined archives..."
fi
# Per-architecture archives
for dir in packages/packages-*/; do
[[ -d "$dir" ]] || continue
ARCH=$(basename "$dir" | sed 's/packages-//')
echo "📦 $ARCH"
# Copy .ipk files to release
mkdir -p "release/${ARCH}"
cp "$dir"/*.ipk "release/${ARCH}/" 2>/dev/null || true
# Create archive
if ls "release/${ARCH}"/*.ipk >/dev/null 2>&1; then
tar -czf "release/${PREFIX}-${VERSION}-${ARCH}.tar.gz" -C "release/${ARCH}" .
fi
done
# Create all-in-one archive
tar -czf "release/${PREFIX}-${VERSION}-all-architectures.tar.gz" -C packages .
# Checksums
cd release
sha256sum *.tar.gz > SHA256SUMS 2>/dev/null || true
echo ""
echo "📋 Release contents:"
ls -la
echo ""
echo "📊 Package count per architecture:"
for dir in */; do
[[ -d "$dir" ]] || continue
COUNT=$(ls "$dir"/*.ipk 2>/dev/null | wc -l)
echo " ${dir%/}: $COUNT packages"
done
- 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
- name: Upload individual .ipk files
uses: actions/upload-artifact@v4
with:
name: secubox-all-ipk-${{ needs.setup.outputs.version }}
path: release/*/*.ipk
retention-days: 90
# ============================================
# Create GitHub Release (on tags or manual)
# ============================================
release:
needs: [setup, build, publish-artifacts]
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch'
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Download release artifact
uses: actions/download-artifact@v4
with:
name: secubox-release-${{ needs.setup.outputs.version }}
path: release
- name: Download all .ipk files
uses: actions/download-artifact@v4
with:
name: secubox-all-ipk-${{ needs.setup.outputs.version }}
path: ipk-files
continue-on-error: true
- name: List packages
id: list-packages
run: |
echo "📦 Packages in release:"
find . -name "*.ipk" -exec basename {} \; | sort -u
# Count unique packages
PKG_LIST=$(find . -name "*.ipk" -exec basename {} \; | sort -u | sed 's/_[0-9].*//g' | sort -u | tr '\n' ', ' | sed 's/,$//')
echo "packages=$PKG_LIST" >> $GITHUB_OUTPUT
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
name: ${{ github.event.inputs.package_name && format('{0} {1}', github.event.inputs.package_name, needs.setup.outputs.version) || format('SecuBox {0}', needs.setup.outputs.version) }}
tag_name: ${{ startsWith(github.ref, 'refs/tags/') && github.ref_name || format('v{0}', needs.setup.outputs.version) }}
body: |
## 📦 ${{ github.event.inputs.package_name && format('{0} Package', github.event.inputs.package_name) || 'SecuBox Packages' }} v${{ needs.setup.outputs.version }}
Pre-built LuCI package${{ github.event.inputs.package_name && '' || 's' }} for OpenWrt ${{ env.OPENWRT_VERSION }}.
${{ github.event.inputs.package_name && format('🎯 **Single package build**: {0}', github.event.inputs.package_name) || '' }}
### ✅ Built Packages
${{ steps.list-packages.outputs.packages }}
### 📥 Installation
```bash
# Download the archive for your architecture
# Extract and upload .ipk files to router
opkg update
opkg install /tmp/luci-app-*.ipk
# Restart services
/etc/init.d/rpcd restart
```
### 🏗️ Supported Architectures
- `x86-64` - PC, VMs, Proxmox
- `aarch64-cortex-a72` - MOCHAbin, RPi4
- `aarch64-cortex-a53` - ESPRESSObin
- `aarch64-generic` - Generic ARM64
- `mips-24kc` - TP-Link, ath79
- `mipsel-24kc` - Xiaomi, GL.iNet
- `mediatek-filogic` - MT7981/MT7986
### 🔗 Links
- [SecuBox Website](https://secubox.cybermood.eu)
- [CyberMind.fr](https://cybermind.fr)
files: |
release/*.tar.gz
release/SHA256SUMS
draft: false
prerelease: ${{ github.event_name == 'workflow_dispatch' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# ============================================
# Final Summary
# ============================================
summary:
needs: [setup, build]
runs-on: ubuntu-latest
if: always()
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
with:
path: packages
pattern: packages-*
continue-on-error: true
- name: Generate summary
run: |
PACKAGE_NAME="${{ github.event.inputs.package_name }}"
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 "| Triggered by | ${{ github.event_name }} |" >> $GITHUB_STEP_SUMMARY
if [[ -n "$PACKAGE_NAME" ]]; then
echo "| Package | 🎯 $PACKAGE_NAME (single package build) |" >> $GITHUB_STEP_SUMMARY
else
echo "| Package | 📦 All packages |" >> $GITHUB_STEP_SUMMARY
fi
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
TOTAL=0
for dir in packages/packages-*/; do
if [[ -d "$dir" ]]; then
ARCH=$(basename "$dir" | sed 's/packages-//')
COUNT=$(find "$dir" -name "*.ipk" 2>/dev/null | wc -l)
TOTAL=$((TOTAL + COUNT))
if [[ $COUNT -gt 0 ]]; then
echo "| $ARCH | ✅ Success | $COUNT |" >> $GITHUB_STEP_SUMMARY
else
echo "| $ARCH | ⚠️ Empty | 0 |" >> $GITHUB_STEP_SUMMARY
fi
fi
done
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Total packages built: $TOTAL**" >> $GITHUB_STEP_SUMMARY