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>
589 lines
28 KiB
YAML
589 lines
28 KiB
YAML
# Shater v0.2 — build the 4-package signed opkg feed and publish it as a rolling
|
|
# Gitea release consumable as an `src/gz` feed.
|
|
#
|
|
# WHAT CHANGED FROM v0.1
|
|
# v0.1 shipped 3 packages: xrayctl (SDK-compiled Go) + shater-core +
|
|
# luci-app-shater (hand-packed data .ipk). v0.2 collapses the runtime into ONE
|
|
# forked binary and ships 4 packages, all built the canonical SDK way:
|
|
# - shaterd PREBUILT static-musl + SPA-embedded + UPX binary. Built
|
|
# OUT OF TREE by scripts/build-shaterd.sh (Go + Node + UPX)
|
|
# and staged into openwrt/shaterd/files/ BEFORE the SDK
|
|
# build; the openwrt/shaterd package just $(INSTALL_BIN)s
|
|
# the arch-matched artifact. (arch-specific .ipk)
|
|
# - shater-core data glue, PKGARCH=all
|
|
# - luci-app-shater LuCI thin launcher, PKGARCH=all (uses feeds/luci/luci.mk)
|
|
# - byedpi ciadpi, C cross-compiled from source by the SDK (arch-specific)
|
|
#
|
|
# TARGET HARDWARE / ARCH MATRIX
|
|
# x86_64 -> the QEMU testbed VM (generic x86-64).
|
|
# aarch64_cortex-a53 -> BOTH production routers (BPI-R3 + BPI-R4, mediatek/filogic).
|
|
# Only shaterd + byedpi are arch-specific; shater-core + luci-app-shater are
|
|
# PKGARCH=all, so one build of each covers every device. opkg filters by
|
|
# Architecture at install time, so a single combined feed URL serves all.
|
|
#
|
|
# FEED SIGNING (opkg / usign — OpenWrt 24.10 is opkg, not apk; apk lands at 25.12)
|
|
# The feed index (Packages) is usign-signed with the SECRET key in the Gitea
|
|
# repo secret KEY_BUILD; routers verify it with the committed public key
|
|
# dist/shater-feed.pub (fingerprint 5ac4b177689cb8e0). Do NOT regenerate the
|
|
# key — that invalidates every deployed router's trust.
|
|
#
|
|
# AUTO-RELEASE
|
|
# push a tag `vX.Y.Z` -> versioned release. workflow_dispatch / (optional) main
|
|
# -> rolling `latest` pre-release (always-fresh feed). Publish uses the Gitea
|
|
# API via curl (ci/gitea-release.sh) — no external action needed.
|
|
#
|
|
# PACKAGE VERSIONING (bug B4)
|
|
# PKG_VERSION/PKG_RELEASE are NOT hand-written in the Makefiles any more. They
|
|
# used to be, and nobody bumped them: v0.2.2…v0.2.6 all shipped as
|
|
# `shaterd 0.2.0-r3` with different binaries inside, so `apk update` never saw
|
|
# a new version and routers could not be updated at all. Now `ci/version.sh`
|
|
# derives them from the git tag ONCE per job (the "Compute version" step,
|
|
# exported via $GITHUB_ENV):
|
|
# tag `vX.Y.Z` -> X.Y.Z-r1
|
|
# anything else -> <nearest tag>-r<commits since it + 1>
|
|
# and hands them to the SDK builds as SHATER_PKG_VERSION/SHATER_PKG_RELEASE;
|
|
# $SHATER_VERSION (the same numbers, plus the short sha off-tag) is stamped
|
|
# into the binary's constant.Version. ci/sdk-build*.sh then ASSERT that the
|
|
# built .ipk/.apk really carry that version, so the failure can never be
|
|
# silent again. This is also why both build jobs check out with fetch-depth: 0
|
|
# — `git describe` needs tags and ancestry. `byedpi` is excluded: it keeps
|
|
# upstream ByeDPI's own PKG_VERSION (see openwrt/byedpi/Makefile).
|
|
#
|
|
# APK LANE (25.12+, ADDITIVE — T2)
|
|
# The fleet is migrating to BananaWRT 25.12-mtk-vendor (= ImmortalWrt 25.12
|
|
# base), where opkg is replaced by Alpine apk (.apk, binary packages.adb
|
|
# index, EC keys in /etc/apk/keys/). The `build-apk` + `release-apk` jobs
|
|
# below build the SAME 4 packages through the ImmortalWrt 25.12 apk-SDK and
|
|
# publish PER-ARCH apk repos as releases `apk-latest-<arch>` (rolling) /
|
|
# `apk-<tag>-<arch>` (versioned). Per-arch because apk filenames carry no
|
|
# architecture (shaterd-0.2.0-r1.apk would collide across arches in one flat
|
|
# release) and apk fetches packages relative to the packages.adb URL.
|
|
# Signed with the EC key in the Gitea secret KEY_APK; trust anchor
|
|
# dist/shater-apk.pem (ci/gen-apk-key.sh). The usign/opkg lane above is
|
|
# UNCHANGED and keeps serving the 24.10 fleet. NOTE: the apk release tags
|
|
# deliberately do NOT start with `v` so publishing them cannot re-trigger
|
|
# this workflow's `v*` tag filter.
|
|
|
|
# CACHING (T3 — fast CI)
|
|
# All caches use actions/cache pinned to v3.3.2: the LAST release speaking the
|
|
# OLD cache API that Gitea's act_runner cache server implements. v4 (and the
|
|
# v3.4.x backports) moved to GitHub's new cache service and fail on act_runner
|
|
# — the same reason upload-artifact is pinned to v3 here. If the runner's
|
|
# cache server is disabled, actions/cache degrades to a warning and the build
|
|
# proceeds uncached (correct, just slower).
|
|
# What is cached, and why each key is safe:
|
|
# - ImmortalWrt SDK tarball (.cache/sdk) — key = tarball basename (carries
|
|
# release + target + gcc), exact-only. Cold-miss fallback chain lives in
|
|
# ci/fetch-sdk.sh: own Gitea release-asset mirror (tag `sdk-cache`) ->
|
|
# upstream with stall-kill + retries; upstream success re-seeds the mirror.
|
|
# - SDK dl/ sources (.cache/dl) — key = hash of openwrt/*/Makefile
|
|
# (PKG_VERSION/PKG_HASH live there). Stale-safe: the buildroot verifies
|
|
# PKG_HASH on every dl/ file and re-downloads on mismatch, so restore-keys
|
|
# prefix fallback is allowed.
|
|
# - Go module + build cache — key = hash of go.sum; shared by all 4 build
|
|
# jobs (each builds both GOARCHes).
|
|
# - panel/node_modules — key = hash of panel/package-lock.json, exact-only
|
|
# (a lockfile change MUST miss); on hit build-shaterd.sh gets --fast.
|
|
# - apt .deb archives for the apk lane's debian:bookworm host-deps
|
|
# (.cache/apt) — key = hash of ci/sdk-build-apk.sh (the apt list is in it).
|
|
# - usign binary (.cache/tools) — static helper, fixed key.
|
|
# - SDK feeds/ git checkouts (.cache/feeds) — the single biggest recurring
|
|
# cost: `scripts/feeds update -a` cloned base+packages+luci+routing+
|
|
# telephony EVERY run (~7 min/job; github.com is ~1 MB/s from this
|
|
# runner — run 51 evidence). The feeds dir is symlinked into the SDK
|
|
# container from the workspace cache; `feeds update` on an existing clone
|
|
# is a fast fetch+checkout of the pinned revs. Correctness-safe: update
|
|
# always checks out feeds.conf's pins, and ci/sdk-build*.sh wipes the
|
|
# cache + re-clones fresh if update ever fails on a cached checkout.
|
|
# Key = lane + SDK release (shared across the two arch jobs of a lane —
|
|
# same release pins identical feed revs; the sequential runner means the
|
|
# second arch restores what the first saved). restore-keys lets an SDK
|
|
# version bump start from the old clones (git fetch delta, not re-clone).
|
|
# Act_runner facts this design leans on (verified in run 51 logs):
|
|
# - the cache backend works: restores/saves confirmed, hashFiles() works;
|
|
# - docker images (openwrt/sdk, debian:bookworm, runner-images) live on the
|
|
# PERSISTENT host daemon — "Image is up to date" each run, no re-download;
|
|
# - each actions/cache SAVE is followed by an exact 3-minute act_runner
|
|
# stall (node process lingers; hit→no-save→no stall). Steady state saves
|
|
# nothing, so adding cache entries is fine, but keys that change every
|
|
# run (e.g. github.sha) would cost +3 min/entry/run — do NOT do that.
|
|
|
|
name: release
|
|
|
|
on:
|
|
push:
|
|
tags: ['v*']
|
|
workflow_dispatch:
|
|
|
|
# A re-dispatch supersedes the still-running build of the same ref: cancel it
|
|
# instead of piling up parallel runs (the user re-triggers often). Tag builds
|
|
# are safe: each tag is its own group, so a release build is only ever cancelled
|
|
# by a re-run of the SAME tag (which supersedes it by definition). Gitea
|
|
# versions without concurrency support ignore this block harmlessly.
|
|
concurrency:
|
|
group: release-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
build:
|
|
name: ${{ matrix.arch }}
|
|
runs-on: ubuntu-latest
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- { arch: x86_64, sdk: x86_64-24.10.4 } # testbed VM (generic x86-64)
|
|
- { arch: aarch64_cortex-a53, sdk: mediatek-filogic-24.10.4 } # BPI-R3 + BPI-R4 (mediatek/filogic)
|
|
steps:
|
|
# fetch-depth: 0 — the package version is DERIVED from the git tag
|
|
# (ci/version.sh: nearest `vX.Y.Z` + commits since it). The default
|
|
# shallow checkout has neither tags nor ancestry, so `git describe` would
|
|
# fail and every dispatch build would fall back to 0.0.0.
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
# scripts/build-shaterd.sh builds the engine via a go.mod
|
|
# `replace => ./submodules/wireguard-go` (AmneziaWG fork), so that submodule
|
|
# must be present or `go build` dies with "no such file or directory".
|
|
# actions/checkout does not fetch submodules by default; init ONLY this one
|
|
# (clients/apple+android are large and unused here).
|
|
- name: Init wireguard-go submodule (awg)
|
|
run: git submodule update --init --depth 1 submodules/wireguard-go
|
|
|
|
# THE version step (bug B4). One computation, used by both the binary
|
|
# (constant.Version) and the three tag-versioned packages, exported to
|
|
# every later step of this job:
|
|
# tag vX.Y.Z -> X.Y.Z-r1 ; off-tag -> <last tag>-r<commits+1>
|
|
- name: Compute version from git tag
|
|
run: bash ci/version.sh --env >> "$GITHUB_ENV"
|
|
|
|
# Toolchain for scripts/build-shaterd.sh: Go (daemon), Node (Vite SPA), UPX.
|
|
- name: Set up Go
|
|
uses: actions/setup-go@v5
|
|
with:
|
|
go-version-file: go.mod # pins Go 1.24.7 (go.mod `go` line)
|
|
cache: false # explicit actions/cache@v3.3.2 below (setup-go's
|
|
# built-in cache uses the new API act_runner lacks)
|
|
|
|
- name: Set up Node
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: '20' # Vite 5 needs Node 18+; 20 LTS
|
|
|
|
# ---- caches (see the header comment for keys + version pin rationale) ----
|
|
- name: Cache Go modules + build cache
|
|
uses: actions/cache@v3.3.2
|
|
with:
|
|
path: |
|
|
~/go/pkg/mod
|
|
~/.cache/go-build
|
|
key: go-${{ hashFiles('go.sum') }}
|
|
restore-keys: |
|
|
go-
|
|
|
|
- name: Cache panel node_modules
|
|
id: npm-cache
|
|
uses: actions/cache@v3.3.2
|
|
with:
|
|
path: panel/node_modules
|
|
key: npm-${{ hashFiles('panel/package-lock.json') }}
|
|
# NO restore-keys: node_modules must exactly match the lockfile;
|
|
# on any lockfile change this misses and `npm ci` runs fresh.
|
|
|
|
- name: Cache SDK dl/ (package sources)
|
|
uses: actions/cache@v3.3.2
|
|
with:
|
|
path: .cache/dl
|
|
key: dl-${{ hashFiles('openwrt/*/Makefile') }}
|
|
restore-keys: |
|
|
dl-
|
|
|
|
# feeds git checkouts (see header): both 24.10.4 arch jobs share one entry
|
|
# (same release = same feeds.conf.default pins), so derive the release
|
|
# from the matrix sdk tag (x86_64-24.10.4 -> 24.10.4).
|
|
- name: Compute feeds cache key
|
|
id: feedskey
|
|
run: echo "ver=$(echo '${{ matrix.sdk }}' | sed 's/.*-//')" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Cache SDK feeds checkouts
|
|
uses: actions/cache@v3.3.2
|
|
with:
|
|
path: .cache/feeds
|
|
key: feeds-opkg-${{ steps.feedskey.outputs.ver }}
|
|
restore-keys: |
|
|
feeds-opkg-
|
|
|
|
- name: Cache CI tools (usign)
|
|
uses: actions/cache@v3.3.2
|
|
with:
|
|
path: .cache/tools
|
|
key: tools-usign-v1
|
|
|
|
- name: Install UPX
|
|
run: sudo apt-get update -qq && sudo apt-get install -y -qq upx-ucl
|
|
|
|
# Build the SPA-embedded, static-musl, UPX'd shaterd for BOTH arches and
|
|
# stage dist/shaterd-<a>.upx into openwrt/shaterd/files/. MUST run before
|
|
# the SDK package build (the openwrt/shaterd package installs the staged
|
|
# artifact). $SHATER_VERSION (from the version step above) is stamped into
|
|
# constant.Version, so the binary and the package agree. On an exact
|
|
# node_modules cache hit, --fast skips the redundant `npm ci`.
|
|
- name: Build & stage shaterd artifact
|
|
env:
|
|
NPM_CACHE_HIT: ${{ steps.npm-cache.outputs.cache-hit }}
|
|
run: |
|
|
set -eu
|
|
FAST=""
|
|
if [ "${NPM_CACHE_HIT:-}" = "true" ]; then FAST="--fast"; fi
|
|
echo "shaterd version: $SHATER_VERSION / package ${SHATER_PKG_VERSION}-r${SHATER_PKG_RELEASE} (npm cache hit: ${NPM_CACHE_HIT:-false})"
|
|
bash scripts/build-shaterd.sh $FAST
|
|
|
|
# Compile the 4 packages through the arch-matched OpenWrt SDK and produce a
|
|
# signed per-arch opkg feed (Packages + Packages.gz + Packages.sig + .ipk).
|
|
# SHATER_PKG_VERSION/SHATER_PKG_RELEASE reach the package Makefiles through
|
|
# the SDK container; ci/sdk-build.sh asserts the .ipk really carry them.
|
|
- name: Build signed feed (SDK)
|
|
env:
|
|
KEY_BUILD: ${{ secrets.KEY_BUILD }}
|
|
run: bash ci/build-feed.sh "${{ matrix.arch }}" "${{ matrix.sdk }}" "out/${{ matrix.arch }}"
|
|
|
|
- name: Show feed
|
|
run: ls -l "out/${{ matrix.arch }}" && cat "out/${{ matrix.arch }}/Packages"
|
|
|
|
- name: Upload feed artifact
|
|
# v4 uses an artifact backend Gitea Actions does not implement
|
|
# (GHESNotSupportedError); v3 works on Gitea's act_runner.
|
|
uses: actions/upload-artifact@v3
|
|
with:
|
|
name: shater-${{ matrix.arch }}
|
|
path: out/${{ matrix.arch }}/*
|
|
if-no-files-found: error
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# APK lane (additive): the same 4 packages through the ImmortalWrt 25.12
|
|
# apk-SDK for the 25.12/apk fleet (BananaWRT 25.12-mtk-vendor routers + the
|
|
# future 25.12 VM). Produces a per-arch apk repo dir: *.apk + EC-signed
|
|
# packages.adb + shater-apk.pem. Artifact prefix `apkfeed-` (NOT `shater-`)
|
|
# so the opkg release job's `artifacts/shater-*` glob never picks these up.
|
|
build-apk:
|
|
name: apk ${{ matrix.arch }}
|
|
runs-on: ubuntu-latest
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
# ImmortalWrt 25.12.1 official SDK tarballs (no immortalwrt/sdk docker
|
|
# tag exists for mediatek-filogic 25.12 — see ci/build-feed-apk.sh).
|
|
- arch: x86_64 # testbed VM
|
|
sdk_url: https://downloads.immortalwrt.org/releases/25.12.1/targets/x86/64/immortalwrt-sdk-25.12.1-x86-64_gcc-14.3.0_musl.Linux-x86_64.tar.zst
|
|
- arch: aarch64_cortex-a53 # BPI-R3 mini (BananaWRT 25.12-mtk-vendor) + BPI-R4
|
|
sdk_url: https://downloads.immortalwrt.org/releases/25.12.1/targets/mediatek/filogic/immortalwrt-sdk-25.12.1-mediatek-filogic_gcc-14.3.0_musl.Linux-x86_64.tar.zst
|
|
steps:
|
|
# fetch-depth: 0 — see the opkg lane: the package version comes from
|
|
# `git describe`, which needs tags + ancestry.
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
# scripts/build-shaterd.sh builds the AmneziaWG-patched wireguard-go via a
|
|
# go.mod `replace => ./submodules/wireguard-go`, so that submodule must be
|
|
# present. actions/checkout does not fetch submodules by default; init ONLY
|
|
# this one (the clients/apple+android submodules are large and unneeded).
|
|
- name: Init wireguard-go submodule (awg)
|
|
run: git submodule update --init --depth 1 submodules/wireguard-go
|
|
|
|
# Same single version computation as the opkg lane — both lanes MUST agree
|
|
# on the version, they package the identical tree.
|
|
- name: Compute version from git tag
|
|
run: bash ci/version.sh --env >> "$GITHUB_ENV"
|
|
|
|
- name: Set up Go
|
|
uses: actions/setup-go@v5
|
|
with:
|
|
go-version-file: go.mod
|
|
cache: false # explicit actions/cache@v3.3.2 below
|
|
|
|
- name: Set up Node
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: '20'
|
|
|
|
# ---- caches (see the header comment for keys + version pin rationale) ----
|
|
- name: Cache Go modules + build cache
|
|
uses: actions/cache@v3.3.2
|
|
with:
|
|
path: |
|
|
~/go/pkg/mod
|
|
~/.cache/go-build
|
|
key: go-${{ hashFiles('go.sum') }}
|
|
restore-keys: |
|
|
go-
|
|
|
|
- name: Cache panel node_modules
|
|
id: npm-cache
|
|
uses: actions/cache@v3.3.2
|
|
with:
|
|
path: panel/node_modules
|
|
key: npm-${{ hashFiles('panel/package-lock.json') }}
|
|
|
|
- name: Cache SDK dl/ (package sources)
|
|
uses: actions/cache@v3.3.2
|
|
with:
|
|
path: .cache/dl
|
|
key: dl-${{ hashFiles('openwrt/*/Makefile') }}
|
|
restore-keys: |
|
|
dl-
|
|
|
|
- name: Cache apt archives (bookworm host-deps)
|
|
uses: actions/cache@v3.3.2
|
|
with:
|
|
path: .cache/apt
|
|
key: apt-bookworm-${{ hashFiles('ci/sdk-build-apk.sh') }}
|
|
restore-keys: |
|
|
apt-bookworm-
|
|
|
|
# The ~300 MB SDK tarball was re-downloaded EVERY run from
|
|
# downloads.immortalwrt.org, which flakes/stalls (>40 min hangs). Cache it
|
|
# by tarball basename (exact key: an SDK version bump = clean miss); on a
|
|
# cold miss ci/fetch-sdk.sh falls back to our own `sdk-cache` release-asset
|
|
# mirror, then to upstream with stall-kill + retries (and re-seeds the
|
|
# mirror) — so even with a dead cache server this never wedges the run.
|
|
- name: Compute SDK cache key
|
|
id: sdkkey
|
|
run: |
|
|
echo "tarball=$(basename '${{ matrix.sdk_url }}')" >> "$GITHUB_OUTPUT"
|
|
echo "relver=$(echo '${{ matrix.sdk_url }}' | sed -n 's#.*/releases/\([^/]*\)/.*#\1#p')" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Cache ImmortalWrt SDK tarball
|
|
uses: actions/cache@v3.3.2
|
|
with:
|
|
path: .cache/sdk
|
|
key: sdk-${{ steps.sdkkey.outputs.tarball }}
|
|
|
|
# feeds git checkouts (see header) — one entry shared by both apk arch
|
|
# jobs of one ImmortalWrt release (identical feeds.conf.default pins).
|
|
- name: Cache SDK feeds checkouts
|
|
uses: actions/cache@v3.3.2
|
|
with:
|
|
path: .cache/feeds
|
|
key: feeds-apk-${{ steps.sdkkey.outputs.relver }}
|
|
restore-keys: |
|
|
feeds-apk-
|
|
|
|
- name: Install UPX
|
|
run: sudo apt-get update -qq && sudo apt-get install -y -qq upx-ucl
|
|
|
|
# Same artifact-order contract as the opkg lane: the SPA-embedded shaterd
|
|
# binary is built OUT of the SDK and staged before the package build.
|
|
- name: Build & stage shaterd artifact
|
|
env:
|
|
NPM_CACHE_HIT: ${{ steps.npm-cache.outputs.cache-hit }}
|
|
run: |
|
|
set -eu
|
|
FAST=""
|
|
if [ "${NPM_CACHE_HIT:-}" = "true" ]; then FAST="--fast"; fi
|
|
echo "shaterd version: $SHATER_VERSION / package ${SHATER_PKG_VERSION}-r${SHATER_PKG_RELEASE} (npm cache hit: ${NPM_CACHE_HIT:-false})"
|
|
bash scripts/build-shaterd.sh $FAST
|
|
|
|
# Compile the 4 packages as .apk through the ImmortalWrt 25.12 SDK and
|
|
# sign the per-arch packages.adb with the EC key (secret KEY_APK).
|
|
# MIRROR_TOKEN lets ci/fetch-sdk.sh seed the `sdk-cache` mirror release
|
|
# after a (rare) upstream download — best-effort, never fails the build.
|
|
- name: Build signed apk feed (ImmortalWrt 25.12 SDK)
|
|
env:
|
|
KEY_APK: ${{ secrets.KEY_APK }}
|
|
MIRROR_TOKEN: ${{ secrets.RELEASE_TOKEN != '' && secrets.RELEASE_TOKEN || github.token }}
|
|
run: bash ci/build-feed-apk.sh "${{ matrix.arch }}" "${{ matrix.sdk_url }}" "out-apk/${{ matrix.arch }}"
|
|
|
|
- name: Show apk feed
|
|
run: ls -l "out-apk/${{ matrix.arch }}"
|
|
|
|
- name: Upload apk feed artifact
|
|
uses: actions/upload-artifact@v3
|
|
with:
|
|
name: apkfeed-${{ matrix.arch }}
|
|
path: out-apk/${{ matrix.arch }}/*
|
|
if-no-files-found: error
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Publish once both arches are built. Rolling `latest` on dispatch, a versioned
|
|
# release on a `vX.Y.Z` tag. Self-contained (curl -> Gitea API).
|
|
release:
|
|
name: release
|
|
needs: build
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Download all arch feeds
|
|
uses: actions/download-artifact@v3
|
|
with:
|
|
path: artifacts
|
|
|
|
- name: Assemble release assets
|
|
id: assets
|
|
run: |
|
|
set -eu
|
|
mkdir -p release
|
|
# For each downloaded arch feed: one ready-to-serve tarball + loose ipks.
|
|
for d in artifacts/shater-*; do
|
|
[ -d "$d" ] || continue
|
|
arch="${d#artifacts/shater-}"
|
|
tar -C "$d" -czf "release/shater-feed-${arch}.tar.gz" .
|
|
# loose .ipk for direct `opkg install <url>` (dedupe shared _all ipks by name)
|
|
for ipk in "$d"/*.ipk; do
|
|
[ -e "$ipk" ] || continue
|
|
cp -n "$ipk" "release/$(basename "$ipk")"
|
|
done
|
|
done
|
|
# ship the feed's public key so routers can verify (see docs-shater/INSTALL.md)
|
|
cp -f dist/shater-feed.pub release/shater-feed.pub
|
|
ls -l release
|
|
echo "count=$(ls release | wc -l)" >> "$GITHUB_OUTPUT"
|
|
|
|
# restore the prebuilt usign binary (skips apt + cmake + clone + build)
|
|
- name: Cache CI tools (usign)
|
|
uses: actions/cache@v3.3.2
|
|
with:
|
|
path: .cache/tools
|
|
key: tools-usign-v1
|
|
|
|
- name: Install usign (feed signer)
|
|
run: bash ci/install-usign.sh
|
|
|
|
- name: Build & sign combined opkg feed index
|
|
# One Packages/Packages.gz over ALL loose .ipk (every arch + arch=all),
|
|
# with basename Filenames. opkg filters by Architecture, so a single
|
|
# release URL serves every device: BPI routers pick aarch64_cortex-a53 +
|
|
# all, the x86 testbed picks x86_64 + all. Signed with KEY_BUILD so
|
|
# routers keep check_signature on. This is what makes the release directly
|
|
# consumable as an `src/gz` feed (see docs-shater/INSTALL.md).
|
|
env:
|
|
KEY_BUILD: ${{ secrets.KEY_BUILD }}
|
|
run: bash ci/make-index.sh release
|
|
|
|
- name: Determine release identity
|
|
id: rel
|
|
run: |
|
|
set -eu
|
|
if [ "${GITHUB_REF#refs/tags/}" != "$GITHUB_REF" ]; then
|
|
echo "tag=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
|
|
echo "name=shater ${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
|
|
echo "prerelease=false" >> "$GITHUB_OUTPUT"
|
|
echo "rolling=false" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo "tag=latest" >> "$GITHUB_OUTPUT"
|
|
echo "name=shater latest (main)" >> "$GITHUB_OUTPUT"
|
|
echo "prerelease=true" >> "$GITHUB_OUTPUT"
|
|
echo "rolling=true" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
|
|
- name: Publish Gitea release
|
|
env:
|
|
TOKEN: ${{ secrets.RELEASE_TOKEN != '' && secrets.RELEASE_TOKEN || github.token }}
|
|
TAG: ${{ steps.rel.outputs.tag }}
|
|
NAME: ${{ steps.rel.outputs.name }}
|
|
PRERELEASE: ${{ steps.rel.outputs.prerelease }}
|
|
ROLLING: ${{ steps.rel.outputs.rolling }}
|
|
BODY: |
|
|
Automated build. Packages: shaterd + byedpi (per-arch), shater-core +
|
|
luci-app-shater (arch=all).
|
|
Targets: x86_64 (testbed) and aarch64_cortex-a53 (BPI-R3 + BPI-R4, mediatek/filogic).
|
|
|
|
── Add as an opkg feed (recommended — then updating is one command) ──
|
|
This release is itself a SIGNED package feed; opkg filters by
|
|
architecture, so the same lines work on every device:
|
|
wget -O /etc/opkg/keys/5ac4b177689cb8e0 https://git.qomar.pw/omar/shater/releases/download/latest/shater-feed.pub
|
|
echo "src/gz shater https://git.qomar.pw/omar/shater/releases/download/latest" >> /etc/opkg/customfeeds.conf
|
|
opkg update
|
|
opkg install luci-app-shater # pulls shater-core + shaterd too
|
|
The public-key install is one-time; after it, `opkg update/upgrade`
|
|
verify the signature with check_signature left on. Full guide: docs-shater/INSTALL.md.
|
|
|
|
── Update (name our packages — never a bare `opkg upgrade`) ──
|
|
opkg update
|
|
opkg upgrade shaterd shater-core luci-app-shater byedpi
|
|
|
|
── Or install the loose .ipk directly / from the tarball feed ──
|
|
wget -O /tmp/f.tgz <this release>/shater-feed-aarch64_cortex-a53.tar.gz
|
|
mkdir -p /tmp/shater && tar -C /tmp/shater -xzf /tmp/f.tgz
|
|
opkg install /tmp/shater/luci-app-shater_*_all.ipk
|
|
run: bash ci/gitea-release.sh release/*
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Publish the apk lane: ONE release PER ARCH (apk package filenames carry no
|
|
# arch, and apk fetches `<name>-<ver>.apk` relative to the packages.adb URL —
|
|
# a flat multi-arch release would collide). Rolling `apk-latest-<arch>` on
|
|
# dispatch, `apk-<tag>-<arch>` on a version tag. The tags do NOT match the
|
|
# workflow's `v*` trigger, so publishing them cannot re-trigger the build.
|
|
release-apk:
|
|
name: release apk
|
|
needs: build-apk
|
|
# Publish whatever arch feeds succeeded — do NOT block the aarch64 release
|
|
# when an unrelated arch (e.g. x86_64) fails. download-artifact only fetches
|
|
# artifacts that exist, and the publish loop skips missing apkfeed-* dirs.
|
|
if: ${{ !cancelled() }}
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Download all arch apk feeds
|
|
uses: actions/download-artifact@v3
|
|
with:
|
|
path: artifacts
|
|
|
|
- name: Determine release identity
|
|
id: rel
|
|
run: |
|
|
set -eu
|
|
if [ "${GITHUB_REF#refs/tags/}" != "$GITHUB_REF" ]; then
|
|
echo "ver=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
|
|
echo "prerelease=false" >> "$GITHUB_OUTPUT"
|
|
echo "rolling=false" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo "ver=latest" >> "$GITHUB_OUTPUT"
|
|
echo "prerelease=true" >> "$GITHUB_OUTPUT"
|
|
echo "rolling=true" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
|
|
- name: Publish per-arch apk releases
|
|
env:
|
|
TOKEN: ${{ secrets.RELEASE_TOKEN != '' && secrets.RELEASE_TOKEN || github.token }}
|
|
VER: ${{ steps.rel.outputs.ver }}
|
|
PRERELEASE: ${{ steps.rel.outputs.prerelease }}
|
|
ROLLING: ${{ steps.rel.outputs.rolling }}
|
|
run: |
|
|
set -eu
|
|
for d in artifacts/apkfeed-*; do
|
|
[ -d "$d" ] || continue
|
|
arch="${d#artifacts/apkfeed-}"
|
|
if [ "$VER" = latest ]; then TAG="apk-latest-$arch"; else TAG="apk-$VER-$arch"; fi
|
|
BODY="Automated apk (OpenWrt/ImmortalWrt 25.12+) package repo for \`$arch\`.
|
|
Packages: shaterd + byedpi (per-arch), shater-core + luci-app-shater (arch=all).
|
|
The index \`packages.adb\` is EC-signed; trust anchor \`shater-apk.pem\` (also in \`dist/\`).
|
|
|
|
── Add as an apk repository ──
|
|
wget -O /etc/apk/keys/shater-apk.pem https://git.qomar.pw/omar/shater/releases/download/$TAG/shater-apk.pem
|
|
echo \"https://git.qomar.pw/omar/shater/releases/download/apk-latest-\$(cat /etc/apk/arch)/packages.adb\" > /etc/apk/repositories.d/shater.list
|
|
apk update
|
|
apk add luci-app-shater # pulls shater-core + shaterd too
|
|
apk add byedpi # optional: ByeDPI desync egress
|
|
── Update — ALWAYS name the packages, NEVER a bare \`apk upgrade\` ──
|
|
apk update
|
|
apk upgrade shaterd shater-core luci-app-shater byedpi
|
|
A bare \`apk upgrade\` reconciles EVERY installed package against every
|
|
configured repo and can downgrade unrelated system packages; naming them
|
|
upgrades only those (apk-tools 3: \"If list of packages is provided, only
|
|
those packages are upgraded along with needed dependencies\").
|
|
Full guide: docs-shater/INSTALL.md §6. The opkg/24.10 feed lives in the \`latest\` release."
|
|
echo "[release-apk] publishing $TAG from $d"
|
|
TAG="$TAG" NAME="shater apk $VER ($arch)" BODY="$BODY" \
|
|
PRERELEASE="$PRERELEASE" ROLLING="$ROLLING" \
|
|
bash ci/gitea-release.sh "$d"/*
|
|
done
|