Files
shater/ci/sdk-build.sh
T
omarandClaude Opus 4.8 8980a25a59 perf(ci): cache SDK feeds checkouts — the biggest recurring build cost
Audit of the run-51 logs showed actions/cache@v3.3.2 works on the act_runner
(cold: "Cache saved" x4; next job: "Cache restored" in ~2s, npm --fast skip,
usign/dl reused) and the sdk-cache mirror seeds correctly — but the single
biggest recurring cost was NOT cached: `scripts/feeds update -a` re-cloned
base+packages+luci+routing+telephony every run (~7.8 min warm x 4 SDK jobs on
the serial runner ≈ ~28 min/run wasted; github ~1 MB/s from this host).

Cache .cache/feeds/{opkg,apk} (workspace dir, actions/cache-persisted, visible
in the SDK container via --volumes-from) symlinked over the SDK's empty feeds/:
`feeds update` now git-fetches deltas (seconds) instead of full clones, always
checking out feeds.conf's pins. Fail-safe: any error on the cached checkouts
wipes the cache and clones fresh. Key by SDK release (feeds-opkg-24.10.4 /
feeds-apk-25.12.1) — stable across runs, invalidates on an SDK bump; both arch
jobs of a lane share one entry (identical pins, serial runner).

Steady-state warm run: ~60+ min -> ~20-22 min. Also documented in the workflow
header: never key a cache on github.sha — each cache SAVE stalls the act_runner
~3 min, so per-run-changing keys would add +3 min/entry every run.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 22:41:53 +03:00

117 lines
5.9 KiB
Bash

#!/bin/sh
# Runs INSIDE an `openwrt/sdk:<target>-<ver>` container (CWD = SDK root
# /builder). The job's workspace is shared into this container via
# `docker run --volumes-from`, so the repo is visible at $REPO and output goes
# to $OUT (a dir under the repo, hence also visible to the runner afterwards).
#
# Unlike Shater v0.1 (which compiled ONLY xrayctl in the SDK and hand-packed the
# pure-data packages with tar), v0.2 builds ALL FOUR packages the canonical way,
# via the SDK feed + `make package/<p>/compile`:
#
# shaterd prebuilt binary — Build/Compile only VALIDATES that
# openwrt/shaterd/files/shaterd-<amd64|arm64>.upx was staged
# by scripts/build-shaterd.sh on the runner BEFORE this ran.
# (arch-specific .ipk: RSTRIP/STRIP disabled — packed ELF.)
# shater-core PKGARCH=all data glue (procd init, sysctl, uci-defaults).
# luci-app-shater PKGARCH=all LuCI thin launcher — its Makefile does
# `include $(TOPDIR)/feeds/luci/luci.mk`, so the `luci` feed
# MUST be updated first (that is what creates feeds/luci/luci.mk).
# byedpi arch-specific C — the SDK cross-compiles ciadpi from the
# upstream tarball (needs network for PKG_SOURCE_URL).
#
# Env (required): ARCH, REPO, OUT.
set -eu
ARCH="${ARCH:?ARCH env required}"
REPO="${REPO:?REPO env required}"
OUT="${OUT:?OUT env required}"
mkdir -p "$OUT"
echo "[sdk] arch=$ARCH repo=$REPO out=$OUT"
test -f "$REPO/openwrt/shaterd/Makefile" || {
echo "[sdk] ERROR: feed not mounted ($REPO/openwrt/shaterd/Makefile missing)"; ls -la "$REPO" || true; exit 9; }
# The prebuilt shaterd artifact must already be staged for this arch.
case "$ARCH" in
x86_64) sfx=amd64 ;;
aarch64_cortex-a53) sfx=arm64 ;;
*) echo "[sdk] ERROR: unsupported ARCH '$ARCH'"; exit 2 ;;
esac
test -f "$REPO/openwrt/shaterd/files/shaterd-$sfx.upx" || {
echo "[sdk] ERROR: openwrt/shaterd/files/shaterd-$sfx.upx not staged."
echo " scripts/build-shaterd.sh must run on the runner before the SDK build."; exit 3; }
# --- register this repo's openwrt/ as a src-link feed named `shater` ---------
# src-link REQUIRES an absolute path; $REPO/openwrt is exactly a feed root (it
# contains the 4 package dirs and nothing else that looks like a package).
cp -f feeds.conf.default feeds.conf
grep -q '^src-link shater ' feeds.conf || echo "src-link shater $REPO/openwrt" >> feeds.conf
# Update metadata for ALL feeds: our `shater` feed + the SDK defaults (base,
# luci, packages, routing, telephony). We need `luci` for feeds/luci/luci.mk and
# `base`/`packages` for the runtime deps (kmod-nft-tproxy, kmod-nft-socket,
# ip-full, rpcd, luci-base) to resolve.
#
# Persistent feeds checkouts: $FEEDS_CACHE (a workspace dir the runner restores
# via actions/cache, shared into this container via --volumes-from) replaces
# the SDK's ephemeral feeds/ dir, so `feeds update` git-fetches deltas instead
# of re-cloning base+packages+luci every run (~7 min on the runner's slow
# github.com link). Correctness-safe: update always checks out feeds.conf's
# pinned revisions; if it ever fails on a cached checkout (e.g. a force-pushed
# upstream), the cache is wiped and the update retried with fresh clones.
if [ -n "${FEEDS_CACHE:-}" ] && mkdir -p "$FEEDS_CACHE" 2>/dev/null; then
rm -rf feeds
ln -s "$FEEDS_CACHE" feeds
echo "[sdk] feeds/ -> $FEEDS_CACHE (persistent cache)"
fi
echo "[sdk] feeds update -a"
if ! ./scripts/feeds update -a; then
[ -L feeds ] || { echo "[sdk] ERROR: feeds update failed"; exit 8; }
echo "[sdk] WARNING: feeds update failed on cached checkouts — wiping cache, cloning fresh"
find "$FEEDS_CACHE" -mindepth 1 -maxdepth 1 -exec rm -rf {} + 2>/dev/null || true
./scripts/feeds update -a
fi
echo "[sdk] feeds install (prefer shater feed)"
./scripts/feeds install -p shater shaterd shater-core byedpi luci-app-shater
# Select our packages, then defconfig. `make package/<p>/compile` builds the
# explicit target regardless, but selecting first makes deps visible to defconfig.
for p in shaterd shater-core byedpi luci-app-shater; do
echo "CONFIG_PACKAGE_$p=m" >> .config
done
# Route source downloads through OpenWrt's fast CDN mirror FIRST — sourceware.org
# (elfutils) and other upstreams intermittently stall mid-transfer, and curl's
# --connect-timeout doesn't cover a stalled stream, so the SDK download hangs the
# build. LOCALMIRROR is tried before each package's own PKG_SOURCE_URL. (lx CI)
echo 'CONFIG_LOCALMIRROR="https://sources.cdn.openwrt.org"' >> .config
# Persistent dl/ across runs: $DL_DIR is a workspace dir the runner restores via
# actions/cache (see ci/build-feed.sh). Correctness-safe: the buildroot verifies
# PKG_HASH on every file already in dl/ and re-downloads on mismatch, so a stale
# cache can never leak a wrong source into the build.
if [ -n "${DL_DIR:-}" ]; then
echo "CONFIG_DOWNLOAD_FOLDER=\"$DL_DIR\"" >> .config
fi
echo "[sdk] defconfig"
make defconfig >/dev/null
# --- compile the 4 packages --------------------------------------------------
for p in shaterd shater-core byedpi luci-app-shater; do
echo "[sdk] === build $p ==="
make "package/$p/compile" V=s -j"$(nproc)"
done
# --- collect ONLY our 4 packages' .ipk (per-arch shaterd/byedpi + _all core/luci)
# NOT `find bin -name '*.ipk'`: the openwrt/sdk image ships HUNDREDS of prebuilt
# kmod/base .ipk under bin/, which a blanket copy would pull into the feed and
# get signed under OUR key. Match each package's own `<name>_<ver>_<arch>.ipk`.
found=0
for p in shaterd shater-core byedpi luci-app-shater; do
for ipk in $(find bin -type f -name "${p}_*.ipk"); do
cp -f "$ipk" "$OUT/"; found=$((found+1))
done
done
[ "$found" -ge 4 ] || { echo "[sdk] ERROR: expected >=4 of OUR .ipk, collected $found"; echo "[sdk] (all .ipk under bin/:)"; find bin -type f -name '*.ipk' | head -20; exit 4; }
chmod -R a+rwX "$OUT" 2>/dev/null || true
echo "[sdk] OK arch=$ARCH — collected $found of our .ipk:"
ls -l "$OUT"