The previous fix only installed Lua via feeds but didn't compile it,
so lua.h headers were still missing from staging_dir. This caused
all LuCI packages to fail compilation with:
fatal error: lua.h: No such file or directory
Root cause: ./scripts/feeds install lua only adds the package to the
build system but doesn't compile it or install headers to staging_dir.
Solution:
1. Install lua package via feeds
2. Enable lua in .config with CONFIG_PACKAGE_lua=m
3. Compile lua package: make package/lua/compile
4. This installs lua.h and other headers to staging_dir/target-*/usr/include/
5. Verify headers are present before continuing
Changes:
- GitHub Actions: Update "Install Lua" step to compile package
- local-build.sh: Update both Lua installation sections (2 places)
- Both: Add verification that lua.h exists in staging_dir
This ensures lucihttp and all LuCI packages can find Lua headers
during compilation, preventing the SDK build failures.
Related: f5c98d9 (previous incomplete fix)
Fixes: #lucihttp-missing-headers
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1010 lines
37 KiB
YAML
1010 lines
37 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'
|
|
- 'secubox-app'
|
|
- 'luci-theme-secubox'
|
|
openwrt_version:
|
|
description: 'OpenWrt version'
|
|
required: true
|
|
default: '24.10.5'
|
|
type: choice
|
|
options:
|
|
- '24.10.5'
|
|
- '25.12.0-rc1'
|
|
- '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
|
|
|
|
# Copy secubox-app
|
|
if [[ -d "package/secubox/secubox-app" ]]; then
|
|
echo " 📁 secubox-app"
|
|
mkdir -p sdk/package/secubox
|
|
cp -r package/secubox/secubox-app sdk/package/secubox/
|
|
|
|
# Update version
|
|
sed -i "s/PKG_VERSION:=.*/PKG_VERSION:=$VERSION/" "sdk/package/secubox/secubox-app/Makefile"
|
|
sed -i "s/PKG_RELEASE:=.*/PKG_RELEASE:=1/" "sdk/package/secubox/secubox-app/Makefile"
|
|
echo " ✓ Fixed version"
|
|
fi
|
|
|
|
# Copy luci-theme-secubox
|
|
if [[ -d "luci-theme-secubox" ]]; then
|
|
echo " 📁 luci-theme-secubox"
|
|
cp -r luci-theme-secubox sdk/package/
|
|
|
|
# Update version
|
|
sed -i "s/PKG_VERSION:=.*/PKG_VERSION:=$VERSION/" "sdk/package/luci-theme-secubox/Makefile"
|
|
sed -i "s/PKG_RELEASE:=.*/PKG_RELEASE:=1/" "sdk/package/luci-theme-secubox/Makefile"
|
|
|
|
# Fix Makefile include path for SDK environment
|
|
sed -i 's|include.*luci\.mk|include $(TOPDIR)/feeds/luci/luci.mk|' "sdk/package/luci-theme-secubox/Makefile"
|
|
echo " ✓ Fixed Makefile include path"
|
|
fi
|
|
fi
|
|
|
|
echo ""
|
|
echo "📋 Packages in SDK:"
|
|
ls -d sdk/package/luci-app-*/ sdk/package/secubox/secubox-app/ sdk/package/luci-theme-secubox/ 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
|
|
|
|
# Download critical LuCI dependencies that fail to compile in SDK
|
|
echo "Downloading lucihttp and related packages..."
|
|
DEPS=("lucihttp" "cgi-io" "lua")
|
|
|
|
for dep in "${DEPS[@]}"; do
|
|
if [[ "$PKG_EXT" == "apk" ]]; then
|
|
# For APK, try to extract package names from index
|
|
echo "Looking for $dep packages..."
|
|
curl -sL "${REPO_BASE}/luci/${dep}*.${PKG_EXT}" -o "${dep}.${PKG_EXT}" 2>/dev/null || true
|
|
curl -sL "${REPO_BASE}/packages/${dep}*.${PKG_EXT}" -o "${dep}.${PKG_EXT}" 2>/dev/null || true
|
|
else
|
|
# For IPK, download from packages feed
|
|
curl -sL "${REPO_BASE}/luci/${dep}_*.${PKG_EXT}" -o "${dep}.${PKG_EXT}" 2>/dev/null || true
|
|
curl -sL "${REPO_BASE}/packages/${dep}*.${PKG_EXT}" -o "${dep}.${PKG_EXT}" 2>/dev/null || true
|
|
fi
|
|
done
|
|
|
|
# Install downloaded packages into SDK
|
|
for pkg in *.${PKG_EXT}; do
|
|
if [[ -f "$pkg" && -s "$pkg" ]]; then
|
|
echo " ✓ Downloaded: $pkg"
|
|
# Copy to SDK packages directory for installation
|
|
cp "$pkg" ../../packages/ 2>/dev/null || true
|
|
fi
|
|
done
|
|
|
|
cd ../..
|
|
|
|
echo ""
|
|
echo "✅ Dependency download completed"
|
|
echo "Note: Our SecuBox packages are PKGARCH:=all (scripts only)"
|
|
echo "They will be built regardless of dependency availability"
|
|
|
|
- name: Install and compile Lua in SDK
|
|
run: |
|
|
cd sdk
|
|
|
|
echo "📦 Installing and compiling Lua to provide headers for lucihttp..."
|
|
|
|
# Install lua package
|
|
./scripts/feeds install lua
|
|
|
|
# Enable lua package for compilation
|
|
echo "CONFIG_PACKAGE_lua=m" >> .config
|
|
|
|
# Compile lua to get headers in staging_dir
|
|
make defconfig
|
|
make package/lua/compile -j$(nproc) V=s || true
|
|
|
|
# Verify headers are available
|
|
if ls staging_dir/target-*/usr/include/lua.h 2>/dev/null; then
|
|
echo "✅ Lua headers successfully installed"
|
|
else
|
|
echo "⚠️ Warning: Lua headers not found, but continuing..."
|
|
fi
|
|
|
|
- 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 secubox-app if present
|
|
if [[ -d "package/secubox/secubox-app" ]]; then
|
|
echo "CONFIG_PACKAGE_secubox-app=m" >> .config
|
|
echo " ✅ secubox-app"
|
|
fi
|
|
|
|
# Enable luci-theme-secubox if present
|
|
if [[ -d "package/luci-theme-secubox" ]]; then
|
|
echo "CONFIG_PACKAGE_luci-theme-secubox=m" >> .config
|
|
echo " ✅ luci-theme-secubox"
|
|
fi
|
|
|
|
# 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
|
|
|
|
# Disable problematic packages that fail to compile in SDK
|
|
# Our SecuBox packages are PKGARCH:=all (scripts) so they don't need these
|
|
# Use OpenWrt standard format for disabling packages
|
|
echo "# CONFIG_PACKAGE_lucihttp is not set" >> .config
|
|
echo "# CONFIG_PACKAGE_cgi-io is not set" >> .config
|
|
|
|
# Enable use of pre-built packages from feeds
|
|
echo "CONFIG_FEED_packages=y" >> .config
|
|
echo "CONFIG_FEED_luci=y" >> .config
|
|
|
|
# Allow missing dependencies (our packages are standalone scripts)
|
|
echo "CONFIG_BROKEN=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
|
|
find sdk/bin -name "secubox-app*.${PKG_EXT}" -exec cp {} artifacts/${{ matrix.target }}/ \; 2>/dev/null || true
|
|
find sdk/bin -name "luci-theme-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
|