This repository has been archived on 2026-06-20. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
linux-kernel-bbrv3/.github/workflows/build.yml
T
Claude 3a58dd45ed Install libdw-dev so dpkg-checkbuilddeps passes (amd64 + arm64)
The kernel's generated debian/control lists libdw-dev:native as an
unconditional Build-Depends, so dpkg-buildpackage aborts via
dpkg-checkbuilddeps before the build starts -- regardless of .config.
Disabling BTF/pahole never satisfied this check.

libdw-dev is just the elfutils libdw headers and does not make pahole
run; dwarves stays absent since DEBUG_INFO_BTF is disabled.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 13:53:42 +08:00

395 lines
17 KiB
YAML

name: 构建带有BBRv3的内核
# Gitea Actions reads workflows from .github/workflows/ (and .gitea/workflows/).
# This workflow targets a self-hosted Gitea instance. Kernel .deb packages are
# published to Gitea's built-in Debian package registry (an apt source), not to
# Releases. arm64 is cross-compiled on the x86_64 runner
# (CROSS_COMPILE=aarch64-linux-gnu-), so only a single ubuntu-latest runner is needed.
on:
workflow_dispatch:
schedule:
# Daily kernel refresh at 20:00 UTC = 04:00 Asia/Shanghai (GMT+8). Gitea/GitHub
# Actions cron is evaluated in UTC. BBRv3 itself stays pinned to the repo patch.
- cron: "0 20 * * *"
env:
# Gitea Debian package registry for this owner, e.g.
# https://git.chilldove.com/api/packages/icePigeon/debian
PKG_REGISTRY: ${{ github.server_url }}/api/packages/${{ github.repository_owner }}/debian
PKG_USER: ${{ github.repository_owner }}
# Optional origin pin for uploads. The kernel image .deb is >128 MB and a 413 can
# come from TWO request-body caps in series: (1) a CDN fronting the registry
# (Cloudflare free/pro = 100 MB, rejected at the edge before the body reaches the
# origin) and (2) the origin reverse proxy's own client_max_body_size (e.g. nginx).
# Set the Gitea repo/org variable PKG_ORIGIN_IP to the registry origin's address
# (the host behind the CDN) and the publish step pins the upload straight there via
# curl --resolve, bypassing cap (1). Cap (2) is server-side: the origin proxy must
# allow client_max_body_size >= the .deb size, or it still 413s after the bypass.
# vars (unlike secrets) ARE available to workflow-level env. Leave empty if no CDN
# fronts uploads.
PKG_ORIGIN_IP: ${{ vars.PKG_ORIGIN_IP }}
# NOTE: PKG_TOKEN (the write:package secret) is declared at JOB level below. The
# secrets context is not available to workflow-level env, so a value set here
# would silently be empty.
permissions:
# Uploads authenticate via the PKG_TOKEN secret, not the auto-injected token,
# so no write permission is requested here.
contents: read
concurrency:
group: bbrv3-kernel-build
cancel-in-progress: true
jobs:
preflight:
runs-on: ubuntu-latest
env:
PKG_TOKEN: ${{ secrets.PKG_TOKEN }}
outputs:
kernel_version: ${{ steps.plan.outputs.kernel_version }}
build_needed: ${{ steps.plan.outputs.build_needed }}
steps:
- name: 检出代码
uses: actions/checkout@v4
- name: 选择 stable 内核并检查 registry
id: plan
run: |
set -euo pipefail
# Track an established stable series, never the freshly-cut top series
# that still equals mainline. See scripts/select-stable-kernel.sh.
version=$(bash scripts/select-stable-kernel.sh)
echo "Selected stable kernel: $version"
echo "kernel_version=$version" >> "$GITHUB_OUTPUT"
# Build unless BOTH arches already have their COMPLETE artifact set
# (kernel image + meta@version + config, plus headers on amd64). A partial
# previous upload counts as incomplete and triggers a rebuild.
missing=0
for da in amd64 arm64; do
if bash scripts/registry-has-all.sh "$PKG_REGISTRY" stable main "$version" "$da"; then
echo "All artifacts for $version ($da) already in registry."
else
missing=1
fi
done
if [ "$missing" -eq 1 ]; then
echo "build_needed=true" >> "$GITHUB_OUTPUT"
echo "At least one arch for $version is incomplete; build will continue."
else
echo "build_needed=false" >> "$GITHUB_OUTPUT"
echo "All arches for $version fully published; ending before build."
fi
build:
needs: preflight
if: needs.preflight.outputs.build_needed == 'true'
strategy:
fail-fast: false
matrix:
include:
# Both targets build on a single x86_64 runner; arm64 is cross-compiled.
# is_primary builds the Architecture:all bbrv3-config package exactly once.
- arch: x86_64
deb_arch: amd64
runs_on: ubuntu-latest
cross_compile: ""
localversion: -bbrv3
is_primary: "true"
- arch: arm64
deb_arch: arm64
runs_on: ubuntu-latest
cross_compile: aarch64-linux-gnu-
localversion: -bbrv3
is_primary: "false"
runs-on: ${{ matrix.runs_on }}
env:
ARCH: ${{ matrix.arch }}
DEB_ARCH: ${{ matrix.deb_arch }}
KERNEL_VERSION: ${{ needs.preflight.outputs.kernel_version }}
PKG_TOKEN: ${{ secrets.PKG_TOKEN }}
steps:
- name: 检出代码
uses: actions/checkout@v4
- name: 检查 registry 是否已发布
id: check_present
run: |
set -euo pipefail
if bash scripts/registry-has-all.sh "$PKG_REGISTRY" stable main "$KERNEL_VERSION" "$DEB_ARCH"; then
echo "BUILD_NEEDED=false" >> "$GITHUB_ENV"
echo "Full artifact set for $KERNEL_VERSION ($DEB_ARCH) present; skipping."
else
echo "BUILD_NEEDED=true" >> "$GITHUB_ENV"
echo "Artifact set for $KERNEL_VERSION ($DEB_ARCH) incomplete; building."
fi
- name: 安装依赖项
if: env.BUILD_NEEDED == 'true'
run: |
sudo apt-get update
# libdw-dev is an unconditional Build-Depends in the kernel's generated
# debian/control (scripts/package/mkdebian), so dpkg-checkbuilddeps aborts
# the build without it -- even though we disable BTF. It's just the elfutils
# libdw headers and does NOT make pahole run.
# dwarves (pahole) stays absent: the build sets CONFIG_DEBUG_INFO_NONE=y and
# DEBUG_INFO_BTF=n (see prepare-kernel-config.sh), so pahole never runs.
# Re-add dwarves if BTF is ever enabled.
sudo apt-get install -y \
git build-essential \
libncurses-dev libssl-dev libelf-dev libdw-dev \
bison bc flex rsync debhelper \
dpkg-dev fakeroot kmod cpio \
lz4 zstd xz-utils curl jq
- name: 安装 ARM64 交叉编译工具链
if: env.BUILD_NEEDED == 'true' && matrix.arch == 'arm64'
run: |
sudo apt-get install -y gcc-aarch64-linux-gnu
- name: 配置 ccache
if: env.BUILD_NEEDED == 'true'
run: |
set -euo pipefail
sudo apt-get install -y ccache
# Wrap the (cross-)compilers through /usr/lib/ccache symlinks; the build
# picks them up because this dir is prepended to PATH below.
sudo update-ccache-symlinks || true
ccache_bin="$(command -v ccache)"
sudo ln -sf "$ccache_bin" /usr/lib/ccache/gcc
if [ "$ARCH" = "arm64" ]; then
sudo ln -sf "$ccache_bin" /usr/lib/ccache/aarch64-linux-gnu-gcc
fi
echo "/usr/lib/ccache" >> "$GITHUB_PATH"
{
echo "CCACHE_DIR=$HOME/.ccache"
echo "CCACHE_MAXSIZE=8G"
echo "CCACHE_COMPRESS=1"
# depend mode: hash the source + its -MD/-MMD header list instead of
# running the preprocessor on every one of ~75k compiler calls. The
# kernel always emits dependency files, so this is safe and trims the
# per-call overhead on both hits and misses.
echo "CCACHE_DEPEND=1"
# Insurance against false misses if the build path or build-time macros
# ever vary between runs (the source is re-cloned fresh each time).
# BASEDIR must be an ancestor of the build CWD (./kernel/linux).
# With the path already stable these are near no-ops, but they cost
# nothing and guard against a path/macro change silently going cold.
echo "CCACHE_BASEDIR=$GITHUB_WORKSPACE/kernel"
echo "CCACHE_NOHASHDIR=1"
echo "CCACHE_SLOPPINESS=time_macros,file_macros,locale,pch_defines"
} >> "$GITHUB_ENV"
- name: 恢复 ccache 缓存
if: env.BUILD_NEEDED == 'true'
uses: actions/cache/restore@v4
with:
# Only the compiler cache is worth persisting: the kernel source is
# re-cloned each run and out/ holds freshly built packages. A unique key
# per run forces a fresh save; restore-keys warm-start from the most recent
# prior build of the same arch (then any build of the same arch). Restore
# and save are split (not the combined actions/cache) because that action's
# post-save is gated by post-if: success() — a failed publish would discard
# the whole compile cache. The matching save runs right after the build.
path: ~/.ccache
key: ccache-${{ matrix.arch }}-${{ needs.preflight.outputs.kernel_version }}-${{ github.run_id }}
restore-keys: |
ccache-${{ matrix.arch }}-${{ needs.preflight.outputs.kernel_version }}-
ccache-${{ matrix.arch }}-
- name: 创建源码目录
if: env.BUILD_NEEDED == 'true'
run: mkdir -p ./kernel/linux
- name: 下载内核源代码
if: env.BUILD_NEEDED == 'true'
working-directory: ./kernel
run: |
branch=$(echo "$KERNEL_VERSION" | grep -oP '^\d+\.\d+')
git clone --depth=1 --branch linux-$branch.y \
https://github.com/gregkh/linux.git linux
- name: 应用 BBRv3 补丁
if: env.BUILD_NEEDED == 'true'
working-directory: ./kernel/linux
run: |
bash "$GITHUB_WORKSPACE/scripts/apply-bbrv3-port.sh"
grep -n "BBR_VERSION" net/ipv4/tcp_bbr.c
- name: 更新 Makefile 中的版本号
if: env.BUILD_NEEDED == 'true'
working-directory: ./kernel/linux
run: |
IFS='.' read -r v p s <<< "$KERNEL_VERSION"
sed -i "s/^VERSION *=.*/VERSION = $v/" Makefile
sed -i "s/^PATCHLEVEL *=.*/PATCHLEVEL = $p/" Makefile
sed -i "s/^SUBLEVEL *=.*/SUBLEVEL = $s/" Makefile
- name: 准备 .config 并禁用证书检查
if: env.BUILD_NEEDED == 'true'
timeout-minutes: 8
working-directory: ./kernel/linux
env:
TARGET_ARCH: ${{ matrix.arch }}
CROSS_COMPILE: ${{ matrix.cross_compile }}
run: |
bash "$GITHUB_WORKSPACE/scripts/prepare-kernel-config.sh" "$TARGET_ARCH"
- name: 构建内核 Debian 包
if: env.BUILD_NEEDED == 'true'
working-directory: ./kernel/linux
env:
TARGET_ARCH: ${{ matrix.arch }}
LOCALVERSION_SUFFIX: ${{ matrix.localversion }}
CROSS_COMPILE: ${{ matrix.cross_compile }}
run: |
if [ "$TARGET_ARCH" = "arm64" ]; then
# Drop the linux-headers package on the arm64 cross build: with
# CONFIG_MODULE_SIG_FORMAT=y its packaging cross-compiles scripts/sign-file
# and would need arm64 OpenSSL dev headers (libssl-dev:arm64) we don't
# install. nokernelheaders is the kernel maintainers' documented remedy.
DEB_BUILD_PROFILES=pkg.linux-upstream.nokernelheaders \
make ARCH=arm64 CROSS_COMPILE="$CROSS_COMPILE" bindeb-pkg -j$(nproc) LOCALVERSION="$LOCALVERSION_SUFFIX" KDEB_COMPRESS=gzip skipdbg=true
else
make bindeb-pkg -j$(nproc) LOCALVERSION="$LOCALVERSION_SUFFIX" KDEB_COMPRESS=gzip skipdbg=true
fi
- name: ccache 统计
if: env.BUILD_NEEDED == 'true'
run: ccache -s
- name: 保存 ccache 缓存
# Save even when a later step fails: the compiler cache is valid the moment
# the build finishes, so a downstream failure (e.g. the registry upload) must
# not discard it. !cancelled() = save on success or failure, but skip
# cancellations (cancel-in-progress) so half-built runs don't bloat the cache.
# Key matches the restore step's primary key above.
if: ${{ !cancelled() && env.BUILD_NEEDED == 'true' }}
uses: actions/cache/save@v4
with:
path: ~/.ccache
key: ccache-${{ matrix.arch }}-${{ needs.preflight.outputs.kernel_version }}-${{ github.run_id }}
- name: 检查 deb 包
if: env.BUILD_NEEDED == 'true'
run: |
if find ./kernel -maxdepth 1 \( -name '*-dbg*.deb' -o -name '*-dbgsym*.deb' \) | grep -q .; then
echo "ERROR: debug deb package was generated."
find ./kernel -maxdepth 1 \( -name '*-dbg*.deb' -o -name '*-dbgsym*.deb' \) -print
exit 1
fi
find ./kernel -maxdepth 1 -name 'linux-*.deb' -print | sort
- name: 构建 meta 与 config 包
if: env.BUILD_NEEDED == 'true'
env:
IS_PRIMARY: ${{ matrix.is_primary }}
run: |
set -euo pipefail
mkdir -p ./out
# Depend on the *actual* image package name, immune to localversion drift.
img_deb=$(find ./kernel -maxdepth 1 -name 'linux-image-*.deb' ! -name '*-dbg*' | sort | head -n1)
if [ -z "$img_deb" ]; then
echo "No linux-image .deb found to base the meta-package on." >&2
exit 1
fi
img_pkg=$(dpkg-deb -f "$img_deb" Package)
echo "Kernel image package: $img_pkg"
# Headers package: present on amd64, absent on the arm64 (nokernelheaders)
# cross build. When present, build-meta-package.sh emits a linux-headers-bbrv3
# meta and makes linux-image-bbrv3 depend on it, so DKMS (e.g. ZFS) always has
# a build tree and the box can't end up image-only / unbootable.
hdr_deb=$(find ./kernel -maxdepth 1 -name 'linux-headers-*.deb' ! -name '*-dbg*' | sort | head -n1)
hdr_pkg=""
if [ -n "$hdr_deb" ]; then
hdr_pkg=$(dpkg-deb -f "$hdr_deb" Package)
echo "Kernel headers package: $hdr_pkg"
else
echo "No linux-headers .deb (expected on arm64); headers meta skipped."
fi
bash scripts/build-meta-package.sh "$KERNEL_VERSION" "$DEB_ARCH" "$img_pkg" ./out "$hdr_pkg"
# bbrv3-config is Architecture:all; build it once (on the primary arch).
if [ "$IS_PRIMARY" = "true" ]; then
bash scripts/build-config-package.sh ./out
fi
- name: 发布前复查是否已发布
if: env.BUILD_NEEDED == 'true'
run: |
set -euo pipefail
if bash scripts/registry-has-all.sh "$PKG_REGISTRY" stable main "$KERNEL_VERSION" "$DEB_ARCH"; then
echo "PUBLISH_NEEDED=false" >> "$GITHUB_ENV"
echo "Full set published while this job was running; skipping upload."
else
echo "PUBLISH_NEEDED=true" >> "$GITHUB_ENV"
echo "Set still incomplete; publishing this build."
fi
- name: 发布到 Gitea Debian registry
if: env.BUILD_NEEDED == 'true' && env.PUBLISH_NEEDED == 'true'
run: |
set -euo pipefail
if [ -z "${PKG_TOKEN:-}" ]; then
echo "PKG_TOKEN secret is required to publish (needs write:package scope)." >&2
exit 1
fi
# The kernel image .deb is >128 MB and can 413 on either of two body caps:
# a CDN edge (Cloudflare free/pro = 100 MB) and the origin proxy's own
# client_max_body_size. If PKG_ORIGIN_IP is set, pin the upload straight to
# that origin so the oversized PUT never touches the CDN (clears the edge
# cap). SNI/Host stay $reg_host, so TLS and package routing are unchanged;
# only the TCP connect target moves. The origin proxy must still be
# configured to accept the .deb size, or it 413s even after the bypass.
reg_host=${PKG_REGISTRY#*://}; reg_host=${reg_host%%/*}
resolve=()
if [ -n "${PKG_ORIGIN_IP:-}" ]; then
resolve=(--resolve "${reg_host}:443:${PKG_ORIGIN_IP}")
echo "Uploading direct to origin ${PKG_ORIGIN_IP}, bypassing any CDN in front of ${reg_host}."
else
echo "PKG_ORIGIN_IP unset: uploading via DNS for ${reg_host}; if a CDN fronts it (e.g. Cloudflare, 100 MB cap) the kernel image will 413. Set the PKG_ORIGIN_IP variable to fix." >&2
fi
# Gitea returns 201/202 on upload, 409 when the file already exists.
upload() {
local f="$1" code
code=$(curl -sS -o /dev/null -w '%{http_code}' "${resolve[@]}" \
--user "$PKG_USER:$PKG_TOKEN" --upload-file "$f" \
"$PKG_REGISTRY/pool/stable/main/upload" || echo 000)
case "$code" in
201|202) echo "Uploaded $(basename "$f") ($code)";;
409) echo "Already present $(basename "$f") (409)";;
*) echo "Upload failed: $(basename "$f") (HTTP $code)" >&2; return 1;;
esac
}
shopt -s nullglob
# Publish the kernel image and (x86_64) headers, plus the meta/config
# packages. Never linux-libc-dev (clashes with the distro's userspace
# headers) and never *-dbg* packages.
assets=()
for f in ./kernel/linux-image-*.deb ./kernel/linux-headers-*.deb; do
case "$f" in *-dbg*) continue;; esac
assets+=("$f")
done
assets+=( ./out/*.deb )
if [ ${#assets[@]} -eq 0 ]; then
echo "No .deb assets found to upload." >&2
exit 1
fi
for f in "${assets[@]}"; do
echo "Publishing $(basename "$f")"
upload "$f"
done
echo "Published ${#assets[@]} package(s) for $KERNEL_VERSION ($DEB_ARCH)."