- Add ninja-build to package build workflow (build-openwrt-packages.yml) - Add ninja-build to image build workflow (build-secubox-images.yml) - Update secubox-tools/README.md with ninja-build dependency - Update secubox-tools/local-build.sh dependency check and install instructions - Ninja is required by OpenWrt build system for some compilation tasks This resolves missing ninja errors in GitHub Actions builds.
909 lines
32 KiB
YAML
909 lines
32 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: '24.10.5'
|
|
type: choice
|
|
options:
|
|
- '25.12.0-rc1'
|
|
- '24.10.5'
|
|
- '23.05.5'
|
|
- '23.05.4'
|
|
- 'SNAPSHOT'
|
|
architectures:
|
|
description: 'Architectures (comma-separated or "all")'
|
|
required: false
|
|
default: 'x86-64'
|
|
|
|
env:
|
|
OPENWRT_VERSION: ${{ github.event.inputs.openwrt_version || '24.10.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 ninja-build
|
|
|
|
- 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..."
|
|
|
|
# FIRST: Remove unwanted feeds from feeds.conf.default to prevent indexing errors
|
|
if [[ -f "feeds.conf.default" ]]; then
|
|
sed -i '/telephony/d' feeds.conf.default
|
|
sed -i '/routing/d' feeds.conf.default
|
|
echo "✅ Removed telephony and routing from feeds.conf.default"
|
|
fi
|
|
|
|
# Determine correct branch based on OpenWrt version
|
|
VERSION="${{ env.OPENWRT_VERSION }}"
|
|
if [[ "$VERSION" == "SNAPSHOT" ]]; then
|
|
BRANCH="master"
|
|
elif [[ "$VERSION" =~ ^25\. ]]; then
|
|
BRANCH="openwrt-25.12"
|
|
elif [[ "$VERSION" =~ ^24\. ]]; then
|
|
BRANCH="openwrt-24.10"
|
|
elif [[ "$VERSION" =~ ^23\. ]]; then
|
|
BRANCH="openwrt-23.05"
|
|
else
|
|
BRANCH="openwrt-23.05" # fallback
|
|
fi
|
|
|
|
echo "📌 Using branch: $BRANCH for OpenWrt $VERSION"
|
|
|
|
# Use GitHub mirrors - only essential feeds for SDK
|
|
cat > feeds.conf << FEEDS
|
|
src-git packages https://github.com/openwrt/packages.git;$BRANCH
|
|
src-git luci https://github.com/openwrt/luci.git;$BRANCH
|
|
FEEDS
|
|
|
|
echo "📋 feeds.conf:"
|
|
cat feeds.conf
|
|
echo ""
|
|
|
|
# Update feeds individually with error handling
|
|
echo "🔄 Updating feeds..."
|
|
|
|
FEEDS_OK=0
|
|
REQUIRED_FEEDS=2
|
|
|
|
for feed in packages luci; do
|
|
echo ""
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo "Updating feed: $feed"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
|
|
FEED_SUCCESS=0
|
|
for attempt in 1 2 3; do
|
|
echo "Attempt $attempt of 3..."
|
|
if ./scripts/feeds update $feed 2>&1 | tee feed-update-${feed}.log; then
|
|
if [[ -d "feeds/$feed" ]]; then
|
|
echo " ✅ $feed updated successfully"
|
|
FEEDS_OK=$((FEEDS_OK + 1))
|
|
FEED_SUCCESS=1
|
|
break
|
|
else
|
|
echo " ⚠️ Feed directory not created, retrying..."
|
|
fi
|
|
else
|
|
echo " ⚠️ Update command failed, retrying..."
|
|
fi
|
|
sleep $((10 * attempt))
|
|
done
|
|
|
|
if [[ $FEED_SUCCESS -eq 0 ]]; then
|
|
echo " ❌ Failed to update $feed after 3 attempts"
|
|
echo "Last attempt log:"
|
|
tail -20 feed-update-${feed}.log 2>/dev/null || echo "No log available"
|
|
fi
|
|
done
|
|
|
|
echo ""
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo "📊 Feeds Status: $FEEDS_OK/$REQUIRED_FEEDS updated"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
|
|
# Verify feeds exist before continuing
|
|
if [[ $FEEDS_OK -lt $REQUIRED_FEEDS ]]; then
|
|
echo ""
|
|
echo "❌ ERROR: Not all required feeds were updated successfully"
|
|
echo "SDK feeds directory contents:"
|
|
ls -la feeds/ || echo "feeds directory doesn't exist"
|
|
exit 1
|
|
fi
|
|
|
|
# Install feeds
|
|
echo ""
|
|
echo "📦 Installing feeds..."
|
|
if ! ./scripts/feeds install -a 2>&1 | tee feed-install.log; then
|
|
echo "⚠️ Feed installation had errors, checking if critical..."
|
|
# Continue anyway as some warnings are normal
|
|
fi
|
|
|
|
# Verify critical directories exist
|
|
echo ""
|
|
echo "🔍 Verifying feed installation..."
|
|
for feed in packages luci; do
|
|
if [[ -d "feeds/$feed" ]]; then
|
|
FEED_SIZE=$(du -sh "feeds/$feed" 2>/dev/null | cut -f1)
|
|
echo " ✅ feeds/$feed exists ($FEED_SIZE)"
|
|
else
|
|
echo " ❌ feeds/$feed is missing!"
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
# 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
|
|
|
|
# Final cleanup of unwanted feeds (if somehow they still got created)
|
|
rm -f feeds/telephony.index feeds/routing.index 2>/dev/null || true
|
|
rm -rf feeds/telephony feeds/routing 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"
|
|
|
|
# Fix Makefile include path for SDK environment
|
|
# Change from ../../luci.mk to $(TOPDIR)/feeds/luci/luci.mk
|
|
sed -i 's|include.*luci\.mk|include $(TOPDIR)/feeds/luci/luci.mk|' "sdk/package/${PACKAGE_NAME}/Makefile"
|
|
echo " ✓ Fixed Makefile include path"
|
|
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"
|
|
|
|
# Fix Makefile include path for SDK environment
|
|
# Change from ../../luci.mk to $(TOPDIR)/feeds/luci/luci.mk
|
|
sed -i 's|include.*luci\.mk|include $(TOPDIR)/feeds/luci/luci.mk|' "sdk/package/${PKG_NAME}/Makefile"
|
|
echo " ✓ Fixed Makefile include path"
|
|
fi
|
|
done
|
|
fi
|
|
|
|
echo ""
|
|
echo "📋 Packages in SDK:"
|
|
ls -d sdk/package/luci-app-*/ 2>/dev/null || echo "None"
|
|
|
|
- name: Download pre-built LuCI dependencies
|
|
run: |
|
|
cd sdk
|
|
|
|
echo "📥 Downloading pre-built LuCI dependencies from OpenWrt repository..."
|
|
echo "This avoids compiling lucihttp and cgi-io which fail in SDK environment"
|
|
echo ""
|
|
|
|
# OpenWrt package repository base URL
|
|
VERSION="${{ env.OPENWRT_VERSION }}"
|
|
ARCH="${{ matrix.arch }}"
|
|
|
|
# Detect package format based on OpenWrt version
|
|
if [[ "$VERSION" =~ ^25\. ]] || [[ "$VERSION" == "SNAPSHOT" ]]; then
|
|
PKG_EXT="apk"
|
|
else
|
|
PKG_EXT="ipk"
|
|
fi
|
|
|
|
# Skip for RC versions as repos may not be stable
|
|
if [[ "$VERSION" =~ -rc ]]; then
|
|
echo "⚠️ Skipping dependency download for RC version"
|
|
echo "Note: Our SecuBox packages are PKGARCH:=all (scripts only)"
|
|
echo "They will be built regardless of dependency availability"
|
|
exit 0
|
|
fi
|
|
|
|
REPO_BASE="https://downloads.openwrt.org/releases/${VERSION}/packages/${ARCH}"
|
|
|
|
echo "Repository: $REPO_BASE"
|
|
echo "Package format: .${PKG_EXT}"
|
|
echo ""
|
|
|
|
# Download problematic dependencies as binaries
|
|
mkdir -p dl/luci-deps
|
|
cd dl/luci-deps
|
|
|
|
echo "Downloading LuCI core packages..."
|
|
# Try to download package index (format depends on version)
|
|
if [[ "$PKG_EXT" == "apk" ]]; then
|
|
curl -sL "${REPO_BASE}/luci/APKINDEX.tar.gz" > apkindex_luci.tar.gz || true
|
|
curl -sL "${REPO_BASE}/packages/APKINDEX.tar.gz" > apkindex_base.tar.gz || true
|
|
else
|
|
curl -sL "${REPO_BASE}/luci/Packages" > packages_luci.txt || true
|
|
curl -sL "${REPO_BASE}/packages/Packages" > packages_base.txt || true
|
|
fi
|
|
|
|
# Note: Actual package download logic would go here
|
|
# For now, we just note that dependencies exist
|
|
|
|
cd ../..
|
|
|
|
echo ""
|
|
echo "✅ Download step completed"
|
|
echo "Note: Our SecuBox packages are PKGARCH:=all (scripts only)"
|
|
echo "They will be built regardless of dependency availability"
|
|
|
|
- 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
|
|
|
|
# Enable download of pre-built packages for dependencies
|
|
echo "CONFIG_DEVEL=y" >> .config
|
|
echo "CONFIG_AUTOREBUILD=y" >> .config
|
|
echo "CONFIG_AUTOREMOVE=y" >> .config
|
|
echo "CONFIG_BUILDBOT=y" >> .config
|
|
|
|
make defconfig
|
|
|
|
- name: Build packages
|
|
run: |
|
|
cd sdk
|
|
|
|
# Detect package format based on OpenWrt version
|
|
VERSION="${{ env.OPENWRT_VERSION }}"
|
|
if [[ "$VERSION" =~ ^25\. ]] || [[ "$VERSION" == "SNAPSHOT" ]]; then
|
|
PKG_EXT="apk"
|
|
echo "📦 Building for OpenWrt $VERSION (apk format)"
|
|
else
|
|
PKG_EXT="ipk"
|
|
echo "📦 Building for OpenWrt $VERSION (ipk format)"
|
|
fi
|
|
|
|
# Export for later steps
|
|
echo "PKG_EXT=$PKG_EXT" >> $GITHUB_ENV
|
|
|
|
echo ""
|
|
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"
|
|
|
|
# Our packages are PKGARCH:=all (pure scripts), no compilation needed
|
|
# Try regular build first, fallback to direct packaging if dependencies fail
|
|
if timeout 600 make package/${PKG_NAME}/compile V=s -j1 > "$BUILD_LOG" 2>&1; then
|
|
# Build succeeded, check if package was created (.apk or .ipk)
|
|
PKG_FILE=$(find bin -name "${PKG_NAME}*.${PKG_EXT}" 2>/dev/null | head -1)
|
|
|
|
if [[ -n "$PKG_FILE" ]]; then
|
|
echo "✅ Built: $PKG_NAME"
|
|
echo " → $PKG_FILE"
|
|
BUILT=$((BUILT + 1))
|
|
BUILT_LIST="${BUILT_LIST}${PKG_NAME},"
|
|
else
|
|
echo "⚠️ No .${PKG_EXT} 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 (.${PKG_EXT})"
|
|
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..."
|
|
|
|
PKG_EXT="${{ env.PKG_EXT }}"
|
|
echo "📦 Package format: .${PKG_EXT}"
|
|
|
|
mkdir -p artifacts/${{ matrix.target }}
|
|
|
|
# Find and copy package files (.apk or .ipk)
|
|
find sdk/bin -name "luci-app-*.${PKG_EXT}" -exec cp {} artifacts/${{ matrix.target }}/ \; 2>/dev/null || true
|
|
|
|
# Also collect any SecuBox related packages
|
|
find sdk/bin -name "*secubox*.${PKG_EXT}" -exec cp {} artifacts/${{ matrix.target }}/ \; 2>/dev/null || true
|
|
|
|
# Count packages
|
|
PKG_COUNT=$(find artifacts/${{ matrix.target }} -name "*.${PKG_EXT}" 2>/dev/null | wc -l)
|
|
echo "pkg_count=$PKG_COUNT" >> $GITHUB_OUTPUT
|
|
echo "pkg_ext=$PKG_EXT" >> $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 *.${PKG_EXT} > SHA256SUMS
|
|
fi
|
|
|
|
echo ""
|
|
echo "📦 Total: $PKG_COUNT packages (.${PKG_EXT})"
|
|
|
|
- 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 }}"
|
|
PKG_EXT="${{ steps.collect.outputs.pkg_ext }}"
|
|
|
|
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 "| OpenWrt Version | ${{ env.OPENWRT_VERSION }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Package Format | .$PKG_EXT |" >> $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 }}/*.$PKG_EXT 2>/dev/null | xargs -I{} basename {} | sort >> $GITHUB_STEP_SUMMARY
|
|
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 }}"
|
|
OPENWRT_VERSION="${{ env.OPENWRT_VERSION }}"
|
|
mkdir -p release
|
|
|
|
# Detect package format based on OpenWrt version
|
|
if [[ "$OPENWRT_VERSION" =~ ^25\. ]] || [[ "$OPENWRT_VERSION" == "SNAPSHOT" ]]; then
|
|
PKG_EXT="apk"
|
|
else
|
|
PKG_EXT="ipk"
|
|
fi
|
|
|
|
echo "📦 Package format: .$PKG_EXT"
|
|
|
|
# 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 package files (.apk or .ipk) to release
|
|
mkdir -p "release/${ARCH}"
|
|
cp "$dir"/*.$PKG_EXT "release/${ARCH}/" 2>/dev/null || true
|
|
|
|
# Create archive
|
|
if ls "release/${ARCH}"/*.$PKG_EXT >/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"/*.$PKG_EXT 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 package files
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: secubox-all-packages-${{ needs.setup.outputs.version }}
|
|
path: |
|
|
release/*/*.ipk
|
|
release/*/*.apk
|
|
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 package files
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
name: secubox-all-packages-${{ needs.setup.outputs.version }}
|
|
path: package-files
|
|
continue-on-error: true
|
|
|
|
- name: List packages
|
|
id: list-packages
|
|
run: |
|
|
echo "📦 Packages in release:"
|
|
find . \( -name "*.ipk" -o -name "*.apk" \) -exec basename {} \; | sort -u
|
|
|
|
# Count unique packages
|
|
PKG_LIST=$(find . \( -name "*.ipk" -o -name "*.apk" \) -exec basename {} \; | sort -u | sed 's/_[0-9].*//g' | 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
|
|
|
|
**For OpenWrt 25.12+ (.apk format):**
|
|
```bash
|
|
# Upload .apk files to router
|
|
apk update
|
|
apk add /tmp/luci-app-*.apk
|
|
|
|
# Restart services
|
|
/etc/init.d/rpcd restart
|
|
```
|
|
|
|
**For OpenWrt 24.10 and earlier (.ipk format):**
|
|
```bash
|
|
# 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" -o -name "*.apk" \) 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
|