Compare commits

...
Author SHA1 Message Date
omarandClaude Opus 5 024e9308c9 fix(ci/apk): strip the SDK's generated per-package default m blocks
release / aarch64_cortex-a53 (push) Successful in 3m21s
release / x86_64 (push) Successful in 3m12s
release / apk aarch64_cortex-a53 (push) Successful in 5m32s
release / apk x86_64 (push) Successful in 2m32s
release / release (push) Successful in 8s
release / release apk (push) Successful in 5s
Run 60 settled what runs 58/59 left open. The second pass wrote an explicit
`# CONFIG_PACKAGE_kmod-x is not set` for all 1126 selected kmods and re-ran
defconfig; the count came back 1078, unchanged. The same explicit form DID hold
for CONFIG_ALL/ALL_KMODS/ALL_NONSHARED in the same run.

The difference is prompts. kconfig honours a user value only for symbols that
have one — sym_calc_value ignores S_DEF_USER for a promptless symbol and falls
back to its `default`. ALL* carry prompts in the SDK's Config.in; the blocks
convert-config.pl generates are bare:

    config PACKAGE_kmod-mlx5-core
            tristate
            default m

No value written into .config can turn those off, so remove the `default m`
itself: drop every generated `config PACKAGE_*` block from Config-build.in
before the first defconfig. Nothing is lost — those blocks only replay which
packages the buildbot built. The packages stay declared, with prompts, by the
package tree (tmp/.config-package.in), which is what makes our four selectable
and what `select` acts on; KERNEL_*/LIBC/TOOLCHAIN blocks are untouched, so the
SDK still reproduces its own toolchain settings.

The .config second pass is kept as a cheap backstop (it no-ops once the count
is 0), as are both tripwires.

Verified: bash -n on the file and on the extracted INNER body; the paragraph
delete tested on a synthetic Config-build.in (3 PACKAGE blocks -> 0, KERNEL_*,
LIBC and TOOLCHAINOPTS preserved); the missing-file path exercised under set -eu.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 01:27:37 +03:00
omarandClaude Opus 5 eab1db2c3f fix(ci/apk): second defconfig pass — deselect the SDK's per-kmod default m
release / aarch64_cortex-a53 (push) Successful in 3m19s
release / x86_64 (push) Successful in 3m14s
release / apk aarch64_cortex-a53 (push) Failing after 1m4s
release / apk x86_64 (push) Failing after 1m3s
release / release (push) Successful in 8s
release / release apk (push) Successful in 5s
Turning ALL/ALL_KMODS/ALL_NONSHARED off (22d7161c0) provably worked — run 59
logs all three as `is not set` after defconfig — and changed the kmod count by
exactly zero, 1078 both times. The kmods never came from ALL_KMODS.

They come from the SDK itself. target/sdk/Makefile generates the SDK's
Config-build.in by running convert-config.pl over the BUILDBOT's .config, in
which ALL_KMODS=y had already expanded into one `CONFIG_PACKAGE_kmod-*=m` line
per module. convert-config.pl turns every `CONFIG_X=<val>` line into a symbol
with an unconditional `default <val>`; its `next if /^(# )?CONFIG_PACKAGE/`
filter sits in the `else` branch, which a line containing `=` never reaches.
The SDK therefore ships ~1078 verbatim blocks of `config PACKAGE_kmod-x /
tristate / default m`, none of which consult ALL_KMODS.

Fix: a second pass. The names only exist after kconfig has expanded the tree,
so after the first defconfig rewrite every selected kmod to `is not set` and
re-run defconfig. Two documented kconfig rules make this exact:
  - an explicit value in .config beats a `default` (same rule that kept our
    `# CONFIG_ALL* is not set` lines alive in run 59) -> the ~1078 stay off;
  - `select` is OR-ed in after the user value, so shater-core's
    `DEPENDS:=+kmod-nft-tproxy +kmod-nft-socket` brings those (and their
    transitive kmods) back on their own.

Also correct the tripwire message, which still blamed CONFIG_ALL_KMODS: it now
prints the ALL* state AND the first few surviving kmods, so the two failure
modes are distinguishable at a glance.

Verified: bash -n on the file and on the extracted INNER heredoc body; the
rewrite simulated against a run-59-shaped .config (1078 -> 0 selected, our 4
packages, LOCALMIRROR and the ALL* lines untouched).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 01:16:05 +03:00
omarandClaude Opus 5 22d7161c08 fix(ci/apk): disable the SDK's ALL/ALL_KMODS mass-select
release / aarch64_cortex-a53 (push) Successful in 3m25s
release / x86_64 (push) Successful in 3m14s
release / apk aarch64_cortex-a53 (push) Failing after 1m3s
release / apk x86_64 (push) Failing after 1m3s
release / release (push) Successful in 8s
release / release apk (push) Successful in 6s
Run 58 proved the previous commit aimed at the wrong thing, and the
diagnostics it added are what showed it: "0 lines carried over" plus a
`grep: .config: No such file or directory`, then 1078 kmods selected
anyway (1109 on x86_64). So an SDK tarball ships no top-level .config at
all — there was never a buildbot config for us to be appending to.

The real source is the SDK's OWN top-level Config.in, target/sdk/files/
Config.in, which it carries instead of the main tree's:

    config ALL_NONSHARED ... default ALL
    config ALL_KMODS     ... default ALL
    config ALL           ... default y

In the main tree all three default to n; the SDK flips ALL to y so that
`make world` in a bare SDK builds something. `make defconfig` therefore
selects the whole kernel from ANY .config, empty or not. This is stock
OpenWrt rather than an ImmortalWrt quirk — openwrt/openwrt's copy is
identical, which also means the awg-openwrt reference builds every kmod
too; it just never meets a disk quota on GitHub's runners.

Fix: write all three out as `# CONFIG_X is not set` before defconfig.
They have prompts in the SDK's Config.in, so they are user-settable and
an explicit value beats the default; `CONFIG_X=n` is not reliably
honoured for bools, hence the `is not set` form. Setting all three, not
just the root ALL, keeps this working whichever symbol roots the chain
in a future SDK.

Drops the hand-rolled CONFIG_TARGET_*/CONFIG_KERNEL_* carry-over as
redundant: target/sdk/convert-config.pl bakes the buildbot's non-package
settings into the SDK's generated Config-build.in as kconfig defaults,
so defconfig reproduces them by itself. A soft branch keeps target
identity and CONFIG_USE_APK if some future SDK does ship a .config.

Diagnostics gain a post-defconfig readout of the three mass-select
symbols and, while the list is short, the actual kmods selected — a
count of 0 is not fatal (the router's base feed carries them) but is
worth seeing. Guards and the 200 threshold are unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 00:57:19 +03:00
+179 -40
View File
@@ -133,44 +133,100 @@ fi
echo "[apk-sdk] feeds install (prefer shater feed)" echo "[apk-sdk] feeds install (prefer shater feed)"
./scripts/feeds install -p shater shaterd shater-core byedpi luci-app-shater ./scripts/feeds install -p shater shaterd shater-core byedpi luci-app-shater
# --- .config: build a MINIMAL one FROM SCRATCH (never append to the SDK's) --- # --- strip the SDK's generated per-package `default m` blocks ----------------
# The ImmortalWrt SDK ships the buildbot's fully-expanded .config, and that file # Run 60 settled the question that runs 58 and 59 left open. Writing an explicit
# carries CONFIG_ALL_KMODS=y + CONFIG_ALL_NONSHARED=y (verifiable without the # `# CONFIG_PACKAGE_kmod-x is not set` for all 1126 of them and re-running
# tarball: downloads.immortalwrt.org/releases/25.12.1/targets/*/config.buildinfo). # defconfig deselected exactly nothing: the count came back 1078, unchanged.
# Appending our 4 packages to it left those in place, so `make defconfig` # Meanwhile the very same explicit form DID stick for CONFIG_ALL/ALL_KMODS/
# re-selected EVERY kernel module of the target as =m; our kmod deps then pull in # ALL_NONSHARED. The difference is prompts. kconfig only honours a user value for
# `package/kernel/linux/compile`, which builds and `apk mkpkg`s the whole set — # a symbol that has one (sym_calc_value ignores S_DEF_USER for a promptless
# 3593 kmod-* packages (mlx5, amdgpu, isdn, ...) in the v0.2.2 run. On a runner # symbol and falls back to its `default`), and the ALL* symbols carry prompts in
# under a 64 GB ZFS quota that is a guaranteed `Disk quota exceeded`. # the SDK's own Config.in while these generated blocks are bare:
# #
# Fix (same shape as Slava-Shchipunov/awg-openwrt's "Setup SDK and feeds" step): # config PACKAGE_kmod-mlx5-core
# start the .config EMPTY so kconfig can only pull in what our 4 packages # tristate
# actually select. Carried over from the SDK's .config, and nothing else: # default m
# CONFIG_TARGET_<board>[_<subtarget>]=y — the target choice. An SDK ships #
# exactly one target, so defconfig would almost certainly pick it anyway; # So no value we write into .config can ever turn them off — the fix has to
# but guessing wrong means silently cross-compiling for the wrong arch, and # remove the `default m` itself. That is what this does: drop every generated
# carrying two lines is far cheaper than that failure mode. # `config PACKAGE_*` block from the SDK's Config-build.in before the first
# CONFIG_TARGET_{BOARD,SUBTARGET,ARCH_PACKAGES} — the string identities that # defconfig. Nothing is lost by it — these blocks only replay which packages the
# name the bin/ paths and the package arch; kept so a mismatch is impossible. # BUILDBOT happened to build; the packages themselves are still declared, with
# CONFIG_USE_APK — decides .apk vs .ipk output, i.e. the entire point of this # prompts, by the package tree (tmp/.config-package.in), which is what makes our
# lane. 25.12 defaults it to y, but this lane must not depend on a default. # four selectable and what `select` acts on. KERNEL_*/LIBC/TOOLCHAIN blocks are
# CONFIG_KERNEL_* (both `=y` and the `# ... is not set` form) — these feed # left untouched, so the SDK still reproduces its own toolchain settings.
# straight into the generated kernel .config. The SDK ships a PREBUILT kernel CB=$(find . -maxdepth 2 -name 'Config-build.in' -print -quit 2>/dev/null || true)
# tree, and our two nft kmods are packaged out of it; drop a knob the SDK's if [ -n "$CB" ] && command -v perl >/dev/null 2>&1; then
# kernel was configured with and the buildsystem sees a changed kernel pkg_before=$(grep -c '^config PACKAGE_' "$CB" || true)
# .config, reconfigures and REBUILDS the kernel — exactly the long, disk-hungry # Paragraph-wise delete: a block is `config PACKAGE_x`, its indented body, and
# detour we are trying to avoid. Carrying them verbatim keeps that tree valid. # the blank line that ends it. Anchored per-line (/m) so nothing else matches.
# Deliberately NOT carried: CONFIG_ALL*/CONFIG_PACKAGE_* (the bug itself); perl -0777 -pi -e 's/^config PACKAGE_\S+\n(?:[ \t]+\S[^\n]*\n)+\n//gm' "$CB"
# CONFIG_TARGET_DEVICE_*/MULTI_PROFILE/ALL_PROFILES/PER_DEVICE_ROOTFS (image pkg_after=$(grep -c '^config PACKAGE_' "$CB" || true)
# knobs — we only ever run `package/<p>/compile`, never build an image); and echo "[apk-sdk] $CB: stripped $((pkg_before - pkg_after)) generated PACKAGE default blocks ($pkg_before -> $pkg_after)"
# CONFIG_BUILDBOT/CONFIG_COLLECT_KERNEL_DEBUG/CONFIG_JSON_CYCLONEDX_SBOM, which else
# only add artifacts we neither ship nor have the disk for. echo "[apk-sdk] WARNING: no Config-build.in found (or no perl) — per-package"
grep -E '^CONFIG_TARGET_[a-z0-9_]+=y$|^CONFIG_TARGET_(BOARD|SUBTARGET|ARCH_PACKAGES)=|^CONFIG_USE_APK=|^CONFIG_KERNEL_|^# CONFIG_KERNEL_[A-Za-z0-9_]+ is not set$' \ echo "[apk-sdk] 'default m' blocks stay; the kmod tripwire will catch it"
.config > .config.shater || true fi
echo "[apk-sdk] .config rebuilt from scratch; $(wc -l < .config.shater) lines carried over"
echo "[apk-sdk] (CONFIG_KERNEL_* kept verbatim and not echoed; identity lines:)" # --- .config: turn OFF the SDK's mass-select defaults ------------------------
grep -v '^\(# \)\?CONFIG_KERNEL_' .config.shater | sed 's/^/[apk-sdk] /' || true # Symptom (v0.2.2, and still v0.2.3 run 58): the SDK ran `apk mkpkg` on ~1100
mv -f .config.shater .config # kmod-* packages — mlx5, amdgpu, ata, isdn, none of which we ship — and died
# with `Disk quota exceeded` on the runner's 64 GB ZFS quota. Our kmod deps pull
# in `package/kernel/linux/compile`, which packs every module marked =m.
#
# Why they are =m has nothing to do with anything we write here. An OpenWrt SDK
# carries its OWN top-level Config.in (target/sdk/files/Config.in), and it reads:
#
# config ALL_NONSHARED
# bool "Select all target specific packages by default"
# default ALL
# config ALL_KMODS
# bool "Select all kernel module packages by default"
# default ALL
# config ALL
# bool "Select all userspace packages by default"
# default y <-- y, not n, and ONLY inside the SDK
#
# In the main tree those three default to n; the SDK flips ALL to y so that
# `make world` in a bare SDK builds something useful. So `make defconfig` on ANY
# .config — empty or not — selects the entire kernel. This is stock OpenWrt, not
# an ImmortalWrt quirk: openwrt/openwrt's target/sdk/files/Config.in is identical.
# (It also means the reference we copied, Slava-Shchipunov/awg-openwrt, builds
# every kmod too — it just never hits a disk quota on GitHub's runners.)
#
# Fix: state all three explicitly. They carry prompts in the SDK's Config.in, so
# they are user-settable and an explicit value beats the `default`. Note the FORM:
# kconfig writes a false bool as `# CONFIG_X is not set` and `CONFIG_X=n` is not
# reliably honoured, so `is not set` is the only form used here. All three are set
# rather than just the root `ALL`, so this keeps working whichever symbol a future
# SDK makes the root of the chain.
# Stash anything the SDK shipped (see below — today there is nothing) and start
# from a known-empty file, so what we build here is exactly what we intended.
if [ -s .config ]; then mv -f .config .config.sdk; fi
: > .config
for s in ALL ALL_KMODS ALL_NONSHARED; do
echo "# CONFIG_$s is not set" >> .config
done
# About that stash: an SDK tarball ships NO top-level .config (run 58 logged
# `grep: .config: No such file or directory` — the only `.config` inside the
# tarball is the prebuilt KERNEL's, under the linux dir). This is also why the
# first version of this fix was aimed at the wrong thing: there was never a
# buildbot .config here to append to. Nothing needs carrying over from it either,
# because
# target/sdk/Makefile bakes the buildbot's non-package settings — every
# CONFIG_KERNEL_* included — into the SDK's generated Config-build.in as kconfig
# `default`s (target/sdk/convert-config.pl). defconfig therefore reproduces the
# exact toolchain/kernel settings the SDK was built with, on its own; an earlier
# attempt to copy those lines by hand was redundant and is gone.
# Should a future SDK start shipping a .config, this keeps the two things that
# would then be worth honouring — the target identity and the package format —
# and still lets the lines above override the mass-select.
if [ -s .config.sdk ]; then
echo "[apk-sdk] SDK shipped a .config — carrying over target identity + format:"
grep -E '^CONFIG_TARGET_[a-z0-9_]+=y$|^CONFIG_TARGET_(BOARD|SUBTARGET|ARCH_PACKAGES)=|^CONFIG_USE_APK=' \
.config.sdk | tee -a .config | sed 's/^/[apk-sdk] /' || true
fi
for p in shaterd shater-core byedpi luci-app-shater; do for p in shaterd shater-core byedpi luci-app-shater; do
echo "CONFIG_PACKAGE_$p=m" >> .config echo "CONFIG_PACKAGE_$p=m" >> .config
@@ -190,6 +246,63 @@ fi
echo "[apk-sdk] defconfig" echo "[apk-sdk] defconfig"
make defconfig >/dev/null make defconfig >/dev/null
# --- second pass: deselect the kernel, keep only what our packages select -----
# Turning ALL/ALL_KMODS/ALL_NONSHARED off (above) provably worked — run 59 shows
# all three as `is not set` after defconfig — and changed the kmod count by
# exactly zero, 1078 both times. The kmods are not selected through ALL_KMODS at
# all. They are selected one by one, and here is where from:
#
# target/sdk/Makefile:
# ./convert-config.pl $(TOPDIR)/.config > $(SDK_BUILD_DIR)/Config-build.in
#
# The SDK's Config-build.in is GENERATED from the buildbot's .config — a config
# in which ALL_KMODS=y had already expanded into a `CONFIG_PACKAGE_kmod-*=m` line
# per module. convert-config.pl turns every `CONFIG_X=<val>` line into a kconfig
# symbol carrying an unconditional `default <val>`; its `next if
# /^(# )?CONFIG_PACKAGE/` filter sits in the `else` branch, which a line with an
# `=` in it never reaches. So the SDK ships, verbatim, 1078 blocks of:
#
# config PACKAGE_kmod-mlx5-core
# tristate
# default m
#
# Nothing there consults ALL_KMODS, which is why switching it off was inert.
#
# Fix: give those symbols an explicit user value. We cannot do it before the
# first defconfig — the list of names only exists once kconfig has expanded the
# tree — so this is a second pass: rewrite every selected kmod to `is not set`
# and re-run defconfig. Two kconfig rules make the result exactly what we want,
# and both are already demonstrated in our own logs:
# * an explicit value in .config beats a `default` (this is precisely why the
# `# CONFIG_ALL* is not set` lines survived defconfig in run 59), so the
# ~1078 kmods we do not need stay off;
# * `select` is a reverse dependency, OR-ed into the symbol's value AFTER the
# user value in sym_calc_value(), so it cannot be overridden by an explicit
# `n`. shater-core's `DEPENDS:=+kmod-nft-tproxy +kmod-nft-socket` becomes
# `select PACKAGE_kmod-nft-tproxy` (scripts/package-metadata.pl: a `+` flag
# sets `$m = "select"`, and it re-emits the dependency's own depends too, so
# transitive kmods follow). Those come back on their own.
# Net effect: we build the handful of kmods our packages actually pull in.
#
# Rejected alternatives:
# * limiting what `package/kernel/linux/compile` packs — that target has no
# such knob; it iterates the selected set, so the selection IS the knob;
# * `package/kernel/linux/clean` + a targeted build — the kernel package would
# simply be rebuilt in full as a dependency of shater-core, same cost;
# * copying OpenWrt's own feed CI (openwrt/gh-action-sdk) — it does nothing
# about this; it just runs `make defconfig` and builds. Its one disk-related
# setting, CONFIG_AUTOREMOVE=y, is already the SDK's default;
# * editing the SDK's generated Config-build.in to strip the offending blocks —
# it would work, but it means parsing a generated kconfig file by hand and a
# format change would corrupt it silently. The two-pass approach uses only
# kconfig's documented semantics and leaves the evidence in .config.
kmods_all=$(grep -c '^CONFIG_PACKAGE_kmod-[^=]*=[my]$' .config || true)
if [ "$kmods_all" -gt 0 ]; then
echo "[apk-sdk] deselecting $kmods_all kmod packages, then defconfig again"
sed -i -E 's/^CONFIG_(PACKAGE_kmod-[^=]*)=[my]$/# CONFIG_\1 is not set/' .config
make defconfig >/dev/null
fi
# --- post-defconfig sanity + disk-cost readout ------------------------------- # --- post-defconfig sanity + disk-cost readout -------------------------------
# A failed run leaves a ~27 MB log; digging the cause out of it is miserable, so # A failed run leaves a ~27 MB log; digging the cause out of it is miserable, so
# print the handful of numbers that decide whether this run survives the # print the handful of numbers that decide whether this run survives the
@@ -199,6 +312,25 @@ echo "[apk-sdk] target: board=$(sed -n 's/^CONFIG_TARGET_BOARD=//p' .config)" \
"subtarget=$(sed -n 's/^CONFIG_TARGET_SUBTARGET=//p' .config)" \ "subtarget=$(sed -n 's/^CONFIG_TARGET_SUBTARGET=//p' .config)" \
"arch_packages=$(sed -n 's/^CONFIG_TARGET_ARCH_PACKAGES=//p' .config)" "arch_packages=$(sed -n 's/^CONFIG_TARGET_ARCH_PACKAGES=//p' .config)"
echo "[apk-sdk] kmod packages selected (=m): $kmods" echo "[apk-sdk] kmod packages selected (=m): $kmods"
# Proof the mass-select stayed off: these three must come back out of defconfig
# as `is not set`. If any reads `=y`, the SDK's `default ALL`/`default y` won and
# the kmod count above will be in the four digits.
echo "[apk-sdk] mass-select symbols after defconfig:"
grep -E '^(# )?CONFIG_ALL(_KMODS|_NONSHARED)?[ =]' .config | sed 's/^/[apk-sdk] /' || true
# After the second pass the only kmods left are the ones shater-core's
# `DEPENDS:=+kmod-nft-tproxy +kmod-nft-socket` turns into kconfig `select`s, plus
# whatever those select in turn — a handful. Worth printing verbatim while the
# list is short. A count of 0 is NOT fatal: those kmods ship in the router's own
# base feed, so apk resolves them there; but it would mean the selects did not
# fire, and that is something we want to see in the log rather than guess at.
if [ "$kmods" -le 30 ]; then
grep '^CONFIG_PACKAGE_kmod.*=m' .config | sed 's/^/[apk-sdk] /' || true
fi
# The two cache knobs are written before the first defconfig and have to survive
# both of them — losing DOWNLOAD_FOLDER silently costs us the dl/ cache, and
# losing LOCALMIRROR brings back the sourceware.org stalls. Cheap to just look.
echo "[apk-sdk] cache settings after defconfig:"
grep -E '^CONFIG_(LOCALMIRROR|DOWNLOAD_FOLDER)=' .config | sed 's/^/[apk-sdk] /' || true
echo "[apk-sdk] our packages after defconfig:" echo "[apk-sdk] our packages after defconfig:"
grep -E '^CONFIG_PACKAGE_(shaterd|shater-core|byedpi|luci-app-shater)=' .config \ grep -E '^CONFIG_PACKAGE_(shaterd|shater-core|byedpi|luci-app-shater)=' .config \
| sed 's/^/[apk-sdk] /' || true | sed 's/^/[apk-sdk] /' || true
@@ -221,9 +353,16 @@ done
# packing the kernel before dying on `Disk quota exceeded`. Fail now instead. # packing the kernel before dying on `Disk quota exceeded`. Fail now instead.
[ "$kmods" -le 200 ] || { [ "$kmods" -le 200 ] || {
echo "[apk-sdk] ERROR: $kmods kmod packages selected — that is the whole kernel." echo "[apk-sdk] ERROR: $kmods kmod packages selected — that is the whole kernel."
echo " CONFIG_ALL_KMODS/CONFIG_ALL is back in .config; aborting before" echo " Aborting before this fills the runner's disk. Two causes are"
echo " this fills the runner's disk. Offending lines:" echo " possible, and the lines below tell them apart:"
grep '^CONFIG_ALL' .config || true; exit 11; } echo " (a) the mass-select is back on -> a CONFIG_ALL* line reads =y;"
echo " (b) the second pass did not take -> ALL* are 'is not set' but the"
echo " kmods returned anyway, i.e. the per-kmod 'default m' from the"
echo " SDK's generated Config-build.in outlived our explicit 'n'."
grep -E '^(# )?CONFIG_ALL(_KMODS|_NONSHARED)?[ =]' .config | sed 's/^/ /' || true
echo " first few kmods still selected:"
grep -m5 '^CONFIG_PACKAGE_kmod.*=m' .config | sed 's/^/ /' || true
exit 11; }
for p in shaterd shater-core byedpi luci-app-shater; do for p in shaterd shater-core byedpi luci-app-shater; do
echo "[apk-sdk] === build $p ===" echo "[apk-sdk] === build $p ==="