Files
shater/ci/build-feed.sh
T
omarandClaude Opus 5 a8f2b0f068 ci: derive package versions from the git tag (B4)
PKG_VERSION/PKG_RELEASE were hand-written literals nobody bumped, so
v0.2.2 … v0.2.6 all shipped as `shaterd 0.2.0-r3` with different binaries
inside (v0.2.6's ELF is 5 491 616 B against r2's 5 488 336 B). Both opkg
and apk offer an upgrade only when the feed's version string differs from
the installed one, so `apk update` saw nothing new and the routers could
not be updated through the normal path at all.

ci/version.sh is now the single source of truth. It derives the version
from `git describe`:

    tag `vX.Y.Z`   -> PKG_VERSION=X.Y.Z  PKG_RELEASE=1
    off-tag build  -> nearest tag + PKG_RELEASE=<commits since it> + 1
    no tag/no git  -> 0.0.0-r1 (below everything ever published)

Ordering verified with the real tools, not from memory — apk-tools 3.0.3
(`apk version -t`) and opkg 38eccbb1 (`opkg compare-versions`) agree that
0.2.0-r3 < 0.2.6-r2 < 0.2.6-r10 < 0.2.6-r12 < 0.2.7-r1 < 0.3.0-r1, so a
release always outranks the rolling builds that preceded it and rolling
builds grow monotonically between releases.

The value travels as SHATER_PKG_VERSION/SHATER_PKG_RELEASE in the SDK
build environment of BOTH lanes; the Makefiles keep a literal fallback so
a manual/offline build still works with no CI and no git. Because the
hand-off crosses docker, `su` and make's env import, ci/sdk-build.sh and
ci/sdk-build-apk.sh now ASSERT that the produced .ipk/.apk really carries
that version — the B4 failure mode was a stale version shipping silently,
and that can no longer happen quietly.

The binary agrees with the package: scripts/build-shaterd.sh takes
constant.Version from the same ci/version.sh (vX.Y.Z-rR[-g<sha>]) instead
of its own `git describe`, and the workflow computes it once per job.
Both build jobs now check out with fetch-depth: 0 — `git describe` needs
tags and ancestry, which the default shallow checkout has neither of.

byedpi is deliberately left alone: PKG_VERSION:=0.17.3 is upstream
ByeDPI's own version, what PKG_HASH pins and what tells an operator which
ByeDPI is installed. Stamping our tag on it would also be a downgrade —
every comparator reads 0.2.7 < 0.17.3 (component-wise, 2 < 17), verified.

Docs: INSTALL.md gains §2.1 (the scheme + the ordering evidence), and the
update sections of §5/§6 now explicitly warn against a bare `opkg upgrade`
/ `apk upgrade` and give the targeted form instead, quoting apk-tools 3:
"If list of packages is provided, only those packages are upgraded along
with needed dependencies". README.md and the release bodies match.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 12:32:38 +03:00

107 lines
5.3 KiB
Bash

#!/bin/sh
# ci/build-feed.sh — build the signed opkg feed for ONE arch.
#
# Usage: ci/build-feed.sh <ARCH> <SDK_DOCKER_TAG> <OUTDIR>
# e.g. ci/build-feed.sh x86_64 x86_64-24.10.4 out/x86_64
# ci/build-feed.sh aarch64_cortex-a53 mediatek-filogic-24.10.4 out/aarch64_cortex-a53
#
# This is the reusable per-arch entrypoint the Gitea workflow calls. It runs on
# the CI RUNNER and:
# 1. asserts the prebuilt shaterd binary for this arch was already staged by
# scripts/build-shaterd.sh (into openwrt/shaterd/files/) — proving artifact
# order: SPA+shaterd build BEFORE the SDK package build;
# 2. drives the arch-matched `openwrt/sdk` docker image to compile all 4
# packages (ci/sdk-build.sh) and collect their .ipk into OUTDIR;
# 3. builds + usign-signs the opkg `Packages` index over OUTDIR
# (ci/install-usign.sh + ci/make-index.sh; signs iff $KEY_BUILD is set).
#
# Env:
# KEY_BUILD usign SECRET key (Gitea repo secret). If set, the feed index is
# signed and verifiable by dist/shater-feed.pub (fp 5ac4b177689cb8e0).
# If unset, an UNSIGNED feed is produced (make-index warns).
set -eu
ARCH="${1:?arch required (x86_64 | aarch64_cortex-a53)}"
SDK_TAG="${2:?sdk docker tag required (e.g. x86_64-24.10.4)}"
OUT="${3:?output dir required}"
REPO="$(cd "$(dirname "$0")/.." && pwd)"
mkdir -p "$OUT"; OUT="$(cd "$OUT" && pwd)"
# $OUT is created here as ROOT on the runner, but the nested `openwrt/sdk`
# container runs as the unprivileged `buildbot` (uid 1000) — so it must be able
# to write the collected .ipk into $OUT. World-writable is set HERE (a chmod
# from inside the container, as buildbot, cannot fix a root-owned dir).
chmod 0777 "$OUT"
# --- 0) the prebuilt shaterd binary must already be staged for this arch ------
case "$ARCH" in
x86_64) sfx=amd64 ;;
aarch64_cortex-a53) sfx=arm64 ;;
*) echo "[feed] ERROR: unsupported ARCH '$ARCH'"; exit 2 ;;
esac
if [ ! -f "$REPO/openwrt/shaterd/files/shaterd-$sfx.upx" ]; then
echo "[feed] ERROR: openwrt/shaterd/files/shaterd-$sfx.upx not staged."
echo " Run scripts/build-shaterd.sh BEFORE ci/build-feed.sh." >&2
exit 3
fi
chmod +x "$REPO"/ci/*.sh 2>/dev/null || true
# --- 0.4) package version from the git tag ------------------------------------
# The workflow normally puts these in the job env (ci/version.sh --env >>
# $GITHUB_ENV); recompute here when this script is run standalone so a manual
# `ci/build-feed.sh ...` produces the same versions as CI. They are handed to the
# SDK container below and read by openwrt/*/Makefile (bug B4 — versions used to
# be hand-written literals that nobody bumped, so v0.2.2…v0.2.6 all shipped as
# 0.2.0-r3 and no router could ever see an update).
if [ -z "${SHATER_PKG_VERSION:-}" ] || [ -z "${SHATER_PKG_RELEASE:-}" ]; then
eval "$(sh "$REPO/ci/version.sh" --env)"
fi
echo "[feed] package version: ${SHATER_PKG_VERSION}-r${SHATER_PKG_RELEASE}"
# --- 0.5) persistent dl/ (package source tarballs) ----------------------------
# Workspace dir restored/saved by actions/cache in the workflow and shared into
# the nested SDK container via --volumes-from; becomes CONFIG_DOWNLOAD_FOLDER
# there (ci/sdk-build.sh). PKG_HASH still verifies every file, so a stale cache
# can never produce a wrong build. Must be writable by the container's
# unprivileged buildbot user (same reason as the $OUT chmod above).
DL_DIR="$REPO/.cache/dl"
mkdir -p "$DL_DIR"
chmod -R a+rwX "$DL_DIR" 2>/dev/null || true
# --- 0.6) persistent feeds/ git checkouts -------------------------------------
# Workspace dir restored/saved by actions/cache (key: feeds-opkg-<release>) and
# symlinked over the SDK's feeds/ inside the container (ci/sdk-build.sh), so
# `scripts/feeds update -a` fetches deltas instead of re-cloning base+packages+
# luci from scratch (~7 min/run on this runner's slow github.com link).
# Top-level chmod only: the contents are created by the container's uid-1000
# build user and restored with the same ownership (tar-as-root preserves it).
FEEDS_CACHE="$REPO/.cache/feeds/opkg"
mkdir -p "$FEEDS_CACHE"
chmod a+rwX "$REPO/.cache" "$REPO/.cache/feeds" "$FEEDS_CACHE" 2>/dev/null || true
# --- 1) SDK package build (4 packages) in the arch-matched SDK image ----------
# We drive the `openwrt/sdk` docker image directly (not openwrt/gh-action-sdk):
# on a self-hosted Gitea act_runner the marketplace action fetch can be
# unavailable, and we need a CLEAN single-feed layout. `--volumes-from
# $(hostname)` shares THIS job container's workspace volume into the nested SDK
# container — a bare `-v $PWD:...` points at a host path that does not exist
# under the act_runner DinD setup. (Requires the job to run inside a container,
# which Gitea Actions does by default.)
echo "[feed] SDK build arch=$ARCH image=openwrt/sdk:$SDK_TAG"
docker pull "openwrt/sdk:$SDK_TAG"
docker run --rm --volumes-from "$(hostname)" \
-e ARCH="$ARCH" -e REPO="$REPO" -e OUT="$OUT" -e DL_DIR="$DL_DIR" \
-e FEEDS_CACHE="$FEEDS_CACHE" \
-e SHATER_PKG_VERSION="$SHATER_PKG_VERSION" \
-e SHATER_PKG_RELEASE="$SHATER_PKG_RELEASE" \
"openwrt/sdk:$SDK_TAG" \
sh "$REPO/ci/sdk-build.sh"
# --- 2) index + sign the per-arch feed (usign, KEY_BUILD passed through) -------
sh "$REPO/ci/install-usign.sh"
KEY_BUILD="${KEY_BUILD:-}" bash "$REPO/ci/make-index.sh" "$OUT"
echo "[feed] done arch=$ARCH -> $OUT"
ls -l "$OUT"