Distribute kernel via Gitea Debian apt repo; track stable not mainline

- Publish .deb packages to Gitea's built-in Debian registry (apt source)
  instead of Releases; CI uploads to pool/stable/main and stops creating
  Releases. One apt source line serves amd64 and arm64.
- Add linux-image-bbrv3 meta-package so `apt upgrade` tracks the latest
  stable kernel while older kernels stay installed for fallback. Its
  dependency is the actual built image package name (immune to localversion
  drift).
- Add bbrv3-config package shipping the BBR+CAKE+ECN sysctl drop-in as a
  conffile (applied via postinst), replacing the installer's sysctl write.
- Rewrite install.sh to add the apt source + signing key and install the
  meta package; keep the OS-version gate, status check and uninstall menu,
  plus an apt-mark hold escape hatch.
- select-stable-kernel.sh: pick the highest stable point release whose
  series is below mainline and has a matching BBRv3 patch, so a freshly-cut
  series (latest stable == mainline, e.g. 7.1) is not built. Currently 7.0.12.
- Exclude linux-libc-dev (clashes with the distro's headers) and drop the
  "by Joey" MODULE_DESCRIPTION step and release branding.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Claude
2026-06-17 18:26:05 +08:00
parent d888a9700e
commit b8ccd431ea
7 changed files with 521 additions and 407 deletions
+119 -116
View File
@@ -1,9 +1,9 @@
name: 构建带有BBRv3的内核 name: 构建带有BBRv3的内核
# Gitea Actions reads workflows from .github/workflows/ (and .gitea/workflows/). # Gitea Actions reads workflows from .github/workflows/ (and .gitea/workflows/).
# This workflow targets a self-hosted Gitea instance: release existence checks, # This workflow targets a self-hosted Gitea instance. Kernel .deb packages are
# creation and asset uploads go through the Gitea REST API (curl), not the GitHub # published to Gitea's built-in Debian package registry (an apt source), not to
# `gh` CLI. arm64 is cross-compiled on the x86_64 runner # Releases. arm64 is cross-compiled on the x86_64 runner
# (CROSS_COMPILE=aarch64-linux-gnu-), so only a single ubuntu-latest runner is needed. # (CROSS_COMPILE=aarch64-linux-gnu-), so only a single ubuntu-latest runner is needed.
on: on:
@@ -13,14 +13,18 @@ on:
- cron: "17 3 * * *" - cron: "17 3 * * *"
env: env:
# Gitea auto-injects GITHUB_TOKEN / GITEA_TOKEN into Actions jobs. # Gitea Debian package registry for this owner, e.g.
GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }} # https://git.chilldove.com/api/packages/icePigeon/debian
# Gitea REST API base for this repo, e.g. PKG_REGISTRY: ${{ github.server_url }}/api/packages/${{ github.repository_owner }}/debian
# https://git.chilldove.com/api/v1/repos/icePigeon/linux-kernel-bbrv3 PKG_USER: ${{ github.repository_owner }}
GITEA_API: ${{ github.server_url }}/api/v1/repos/${{ github.repository }} # Dedicated token with write:package scope. The auto-injected GITHUB_TOKEN is
# not guaranteed to carry package-write permission, so this is a repo secret.
PKG_TOKEN: ${{ secrets.PKG_TOKEN }}
permissions: permissions:
contents: write # Uploads authenticate via the PKG_TOKEN secret, not the auto-injected token,
# so no write permission is requested here.
contents: read
concurrency: concurrency:
group: bbrv3-kernel-build group: bbrv3-kernel-build
@@ -31,59 +35,46 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
outputs: outputs:
kernel_version: ${{ steps.plan.outputs.kernel_version }} kernel_version: ${{ steps.plan.outputs.kernel_version }}
raw_kernel_version: ${{ steps.plan.outputs.raw_kernel_version }}
build_needed: ${{ steps.plan.outputs.build_needed }} build_needed: ${{ steps.plan.outputs.build_needed }}
steps: steps:
- name: 查最新内核和已发布版本 - name: 出代码
uses: actions/checkout@v4
- name: 选择 stable 内核并检查 registry
id: plan id: plan
run: | run: |
set -euo pipefail set -euo pipefail
raw_version=$(curl -fsSL https://www.kernel.org/finger_banner \ # Track an established stable series, never the freshly-cut top series
| awk -F: '/latest stable version/ {gsub(/^[ \t]+|[ \t]+$/, "", $2); print $2; exit}') # that still equals mainline. See scripts/select-stable-kernel.sh.
if [ -z "$raw_version" ]; then version=$(bash scripts/select-stable-kernel.sh)
echo "Failed to read latest stable kernel version from kernel.org." >&2 echo "Selected stable kernel: $version"
exit 1
fi
version="$raw_version"
if [[ "$version" =~ ^[0-9]+\.[0-9]+$ ]]; then
version="${version}.0"
fi
if ! [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Unexpected stable kernel version: $raw_version" >&2
exit 1
fi
echo "raw_kernel_version=$raw_version" >> "$GITHUB_OUTPUT"
echo "kernel_version=$version" >> "$GITHUB_OUTPUT" echo "kernel_version=$version" >> "$GITHUB_OUTPUT"
# Returns 0 when a release with the given tag already exists on Gitea. # Already-published check against the registry's apt index (per arch).
release_exists() { pkg_present() {
local tag="$1" code local debarch="$1"
code=$(curl -sS -o /dev/null -w '%{http_code}' \ curl -fsSL ${PKG_TOKEN:+--user "$PKG_USER:$PKG_TOKEN"} \
-H "Authorization: token $GITEA_TOKEN" \ "$PKG_REGISTRY/dists/stable/main/binary-$debarch/Packages" 2>/dev/null \
"$GITEA_API/releases/tags/$tag" || echo 000) | grep -q "^Package: linux-image-$version-bbrv3$"
[ "$code" = "200" ]
} }
missing=0 missing=0
for arch in x86_64 arm64; do for da in amd64 arm64; do
tag="$arch-$version" if pkg_present "$da"; then
if release_exists "$tag"; then echo "linux-image-$version-bbrv3 ($da) already in registry."
echo "$tag already exists."
else else
echo "$tag is missing." echo "linux-image-$version-bbrv3 ($da) missing."
missing=1 missing=1
fi fi
done done
if [ "$missing" -eq 0 ]; then if [ "$missing" -eq 1 ]; then
echo "build_needed=false" >> "$GITHUB_OUTPUT"
echo "All releases for $version already exist; ending workflow before build."
else
echo "build_needed=true" >> "$GITHUB_OUTPUT" echo "build_needed=true" >> "$GITHUB_OUTPUT"
echo "At least one release for $version is missing; build will continue." echo "At least one arch for $version is missing; build will continue."
else
echo "build_needed=false" >> "$GITHUB_OUTPUT"
echo "All arches for $version already published; ending before build."
fi fi
build: build:
@@ -94,39 +85,42 @@ jobs:
matrix: matrix:
include: include:
# Both targets build on a single x86_64 runner; arm64 is cross-compiled. # 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 - arch: x86_64
deb_arch: amd64
runs_on: ubuntu-latest runs_on: ubuntu-latest
cross_compile: "" cross_compile: ""
localversion: -bbrv3 localversion: -bbrv3
is_primary: "true"
- arch: arm64 - arch: arm64
deb_arch: arm64
runs_on: ubuntu-latest runs_on: ubuntu-latest
cross_compile: aarch64-linux-gnu- cross_compile: aarch64-linux-gnu-
localversion: -bbrv3 localversion: -bbrv3
is_primary: "false"
runs-on: ${{ matrix.runs_on }} runs-on: ${{ matrix.runs_on }}
env: env:
ARCH: ${{ matrix.arch }} ARCH: ${{ matrix.arch }}
DEB_ARCH: ${{ matrix.deb_arch }}
KERNEL_VERSION: ${{ needs.preflight.outputs.kernel_version }} KERNEL_VERSION: ${{ needs.preflight.outputs.kernel_version }}
steps: steps:
- name: 查是否已发布 - name: 出代码
id: check_release uses: actions/checkout@v4
env:
TAG: ${{ matrix.arch }}-${{ needs.preflight.outputs.kernel_version }} - name: 检查 registry 是否已发布
id: check_present
run: | run: |
code=$(curl -sS -o /dev/null -w '%{http_code}' \ set -euo pipefail
-H "Authorization: token $GITEA_TOKEN" \ if curl -fsSL ${PKG_TOKEN:+--user "$PKG_USER:$PKG_TOKEN"} \
"$GITEA_API/releases/tags/$TAG" || echo 000) "$PKG_REGISTRY/dists/stable/main/binary-$DEB_ARCH/Packages" 2>/dev/null \
if [ "$code" = "200" ]; then | grep -q "^Package: linux-image-$KERNEL_VERSION-bbrv3$"; then
echo "BUILD_NEEDED=false" >> "$GITHUB_ENV" echo "BUILD_NEEDED=false" >> "$GITHUB_ENV"
echo "$TAG already exists; skipping build." echo "linux-image-$KERNEL_VERSION-bbrv3 ($DEB_ARCH) already published; skipping."
else else
echo "BUILD_NEEDED=true" >> "$GITHUB_ENV" echo "BUILD_NEEDED=true" >> "$GITHUB_ENV"
echo "$TAG does not exist; building latest BBRv3 kernel." echo "linux-image-$KERNEL_VERSION-bbrv3 ($DEB_ARCH) missing; building."
fi fi
- name: 检出代码
if: env.BUILD_NEEDED == 'true'
uses: actions/checkout@v4
- name: 安装依赖项 - name: 安装依赖项
if: env.BUILD_NEEDED == 'true' if: env.BUILD_NEEDED == 'true'
run: | run: |
@@ -162,15 +156,6 @@ jobs:
bash "$GITHUB_WORKSPACE/scripts/apply-bbrv3-port.sh" bash "$GITHUB_WORKSPACE/scripts/apply-bbrv3-port.sh"
grep -n "BBR_VERSION" net/ipv4/tcp_bbr.c grep -n "BBR_VERSION" net/ipv4/tcp_bbr.c
- name: 编译声明
if: env.BUILD_NEEDED == 'true'
working-directory: ./kernel/linux
run: |
grep -v "MODULE_DESCRIPTION" net/ipv4/tcp_bbr.c > net/ipv4/tcp_bbr.c.tmp
mv net/ipv4/tcp_bbr.c.tmp net/ipv4/tcp_bbr.c
echo 'MODULE_DESCRIPTION("TCP BBR v3 (Bottleneck Bandwidth and RTT) - Compiled & Optimized by Joey");' >> net/ipv4/tcp_bbr.c
tail -n 5 net/ipv4/tcp_bbr.c
- name: 更新 Makefile 中的版本号 - name: 更新 Makefile 中的版本号
if: env.BUILD_NEEDED == 'true' if: env.BUILD_NEEDED == 'true'
working-directory: ./kernel/linux working-directory: ./kernel/linux
@@ -219,66 +204,84 @@ jobs:
fi fi
find ./kernel -maxdepth 1 -name 'linux-*.deb' -print | sort find ./kernel -maxdepth 1 -name 'linux-*.deb' -print | sort
- name: 发布前复查是否已发布 - name: 构建 meta 与 config 包
if: env.BUILD_NEEDED == 'true' if: env.BUILD_NEEDED == 'true'
env: env:
TAG: ${{ matrix.arch }}-${{ needs.preflight.outputs.kernel_version }} IS_PRIMARY: ${{ matrix.is_primary }}
run: |
code=$(curl -sS -o /dev/null -w '%{http_code}' \
-H "Authorization: token $GITEA_TOKEN" \
"$GITEA_API/releases/tags/$TAG" || echo 000)
if [ "$code" = "200" ]; then
echo "PUBLISH_NEEDED=false" >> "$GITHUB_ENV"
echo "$TAG was published while this job was running; skipping upload and release."
else
echo "PUBLISH_NEEDED=true" >> "$GITHUB_ENV"
echo "$TAG still missing; publishing this build."
fi
- name: 发布到 Gitea Release
if: env.BUILD_NEEDED == 'true' && env.PUBLISH_NEEDED == 'true'
env:
TAG: ${{ matrix.arch }}-${{ needs.preflight.outputs.kernel_version }}
TARGET_ARCH: ${{ matrix.arch }}
TARGET_SHA: ${{ github.sha }}
run: | run: |
set -euo pipefail set -euo pipefail
body="带有 BBRv3 的最新内核,适用于 $TARGET_ARCH 架构。Compiled & Optimized by Joey." mkdir -p ./out
auth=(-H "Authorization: token $GITEA_TOKEN") # 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)
# Reuse an existing release for this tag, otherwise create it. if [ -z "$img_deb" ]; then
release_id=$(curl -sS "${auth[@]}" "$GITEA_API/releases/tags/$TAG" | jq -r '.id // empty') echo "No linux-image .deb found to base the meta-package on." >&2
if [ -z "$release_id" ]; then exit 1
release_id=$(curl -sS "${auth[@]}" \
-H "Content-Type: application/json" \
-X POST "$GITEA_API/releases" \
-d "$(jq -n \
--arg tag "$TAG" \
--arg target "$TARGET_SHA" \
--arg name "$TAG" \
--arg body "$body" \
'{tag_name:$tag, target_commitish:$target, name:$name, body:$body, draft:false, prerelease:false}')" \
| jq -r '.id // empty')
fi fi
if [ -z "$release_id" ] || [ "$release_id" = "null" ]; then img_pkg=$(dpkg-deb -f "$img_deb" Package)
echo "Failed to resolve or create Gitea release for $TAG" >&2 echo "Kernel image package: $img_pkg"
bash scripts/build-meta-package.sh "$KERNEL_VERSION" "$DEB_ARCH" "$img_pkg" ./out
# 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 curl -fsSL ${PKG_TOKEN:+--user "$PKG_USER:$PKG_TOKEN"} \
"$PKG_REGISTRY/dists/stable/main/binary-$DEB_ARCH/Packages" 2>/dev/null \
| grep -q "^Package: linux-image-$KERNEL_VERSION-bbrv3$"; then
echo "PUBLISH_NEEDED=false" >> "$GITHUB_ENV"
echo "Published while this job was running; skipping upload."
else
echo "PUBLISH_NEEDED=true" >> "$GITHUB_ENV"
echo "Still missing; 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 exit 1
fi fi
# Upload the kernel .deb packages as release assets. # 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}' \
--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 shopt -s nullglob
assets=( ./kernel/linux-*.deb ) # Publish the kernel image and (x86_64) headers, plus the meta/config
if [ "${#assets[@]}" -eq 0 ]; then # packages. Never linux-libc-dev (clashes with the distro's userspace
echo "No .deb assets found to upload for $TAG" >&2 # 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 exit 1
fi fi
for f in "${assets[@]}"; do for f in "${assets[@]}"; do
name=$(basename "$f") echo "Publishing $(basename "$f")"
echo "Uploading $name to release $TAG (id=$release_id)" upload "$f"
curl -fsS "${auth[@]}" \
-X POST "$GITEA_API/releases/$release_id/assets?name=$name" \
-F "attachment=@$f" >/dev/null
done done
echo "Published $TAG with ${#assets[@]} asset(s)." echo "Published ${#assets[@]} package(s) for $KERNEL_VERSION ($DEB_ARCH)."
+54 -23
View File
@@ -1,6 +1,6 @@
# BBRv3 内核安装脚本 # BBRv3 内核安装脚本
一个用于 Debian/Ubuntu VPS 的 BBRv3 内核安装脚本。脚本会自动识别系统架构,从本仓库 Gitea Releases 下载匹配的 BBRv3 内核 `.deb` 包并安装。内核默认启用 BBR 拥塞控制并内置 `sch_cake`安装脚本会写入 `/etc/sysctl.d/99-bbrv3.conf`,把默认队列算法设为 `cake` 并开启 ECN`net.ipv4.tcp_ecn=1`),安装并重启后 BBR + CAKE + ECN 自动生效。 一个用于 Debian/Ubuntu VPS 的 BBRv3 内核分发方案。内核 `.deb` 包发布到本仓库自建 Gitea **Debian package registryapt 源)**。安装脚本会自动识别架构、配置 apt 源并安装 `linux-image-bbrv3` meta-package,之后 `sudo apt upgrade` 即可自动跟随最新 stable 内核(旧内核保留以便回退)。内核默认启用 BBR 拥塞控制并内置 `sch_cake`随包安装的 `bbrv3-config` 会写入 `/etc/sysctl.d/99-bbrv3.conf`,把默认队列算法设为 `cake` 并开启 ECN`net.ipv4.tcp_ecn=1`),安装并重启后 BBR + CAKE + ECN 自动生效。
脚本入口: 脚本入口:
@@ -29,7 +29,7 @@ Debian testing/unstable 如缺少 `VERSION_ID`,脚本会按 `VERSION_CODENAME`
运行脚本后进入交互菜单: 运行脚本后进入交互菜单:
```text ```text
1. 安装或更新 BBR v3 内核(最新版) 1. 安装或更新 BBR v3 内核(最新版apt 源自动跟随
2. 安装指定版本 2. 安装指定版本
3. 检查 BBR v3 状态 3. 检查 BBR v3 状态
4. 卸载 BBR v3 内核 4. 卸载 BBR v3 内核
@@ -40,19 +40,25 @@ Debian testing/unstable 如缺少 `VERSION_ID`,脚本会按 `VERSION_CODENAME`
本项目的构建目标是: 本项目的构建目标是:
```text ```text
BBRv3 补丁固定,内核自动跟随 kernel.org 最新 stable 更新。 BBRv3 补丁固定,内核自动跟随 kernel.org stable 的小版本更新。
``` ```
也就是说,BBR 实现不会在自动构建时偷偷更新;自动更新的是 Linux stable 内核版本。构建流程会把仓库内固定的 BBRv3 patch 应用到最新 stable 内核上 BBR 实现不会在自动构建时偷偷更新;自动更新的是 Linux stable 内核的小版本。
当前 patch 选择规则 **追踪的是成熟 stable,而非 mainline。** 一个新系列(如 `7.1`)刚发布时,kernel.org 的 “latest stable” 会瞬间等于 “latest mainline”——那其实还是尖端,不是经过 `.z` 打磨的 stable。因此构建脚本(`scripts/select-stable-kernel.sh`)只在满足以下**全部**条件的版本里取最高
- 是带三段点号的点版本 `X.Y.Z`(排除刚发布、仅显示为 `X.Y` 的新系列);
- 系列 `X.Y` 严格低于当前 mainline(排除仍等于 mainline 的系列);
- 仓库存在对应的 `patches/bbrv3-linux-X.Y.patch`
当前 patch
```text ```text
linux-7.0.y -> patches/bbrv3-linux-7.0.patch patches/bbrv3-linux-7.0.patch # linux-7.0.y
linux-7.1.y -> patches/bbrv3-linux-7.1.patch patches/bbrv3-linux-7.1.patch # linux-7.1.y
``` ```
一主线系列内的小版本更新自动复用同一 patch,例`7.0.11 -> 7.0.12`。如果内核跳到新的主线系列但仓库内还没有对应 patch,构建会直接失败,避免产出不可验证的内核包 系列小版本更新自动复用同一 patch`7.0.11 -> 7.0.12`)。等新系列出了点版本、且 mainline 前移到再下一个系列,构建会自动接棒到新系列(前提是其 patch 已就位)
## 安装最新版 ## 安装最新版
@@ -64,12 +70,19 @@ linux-7.1.y -> patches/bbrv3-linux-7.1.patch
脚本会: 脚本会:
- 检查系统是否为 Debian/Ubuntu,架构是否为 `x86_64``aarch64` - 检查系统是否为 Debian/Ubuntu,架构是否为 `x86_64``aarch64`,并拦截过旧系统
- 从 Gitea Releases 获取当前架构匹配的最新版本 - 配置 apt 源:导入 registry 签名公钥到 `/etc/apt/keyrings/`,写入 `/etc/apt/sources.list.d/bbrv3.list`
- 下载内核 `.deb` 包,安装并更新引导配置 - `apt-get install linux-image-bbrv3` 安装/升级到最新 stable(旧内核保留以便回退)
- 提示是否重启。 - 提示是否重启。
如果访问私有仓库或遇到 Gitea API 限流,可先设置 token 之后无需再跑脚本,直接 `sudo apt update && sudo apt upgrade` 即可跟随最新内核。**不希望内核被 `apt upgrade` 自动更换**时
```bash
sudo apt-mark hold linux-image-bbrv3 # 钉死,停止自动跟随
sudo apt-mark unhold linux-image-bbrv3 # 解钉,恢复自动跟随
```
如果 registry 为私有或拉取签名公钥受限,可先设置 token 再运行:
```bash ```bash
export GITEA_TOKEN=你的 Gitea Token export GITEA_TOKEN=你的 Gitea Token
@@ -84,15 +97,30 @@ bash <(curl -fsSL https://git.chilldove.com/icePigeon/linux-kernel-bbrv3/raw/bra
2. 安装指定版本 2. 安装指定版本
``` ```
脚本会列出当前架构可用的 release tag,并按编号安装指定版本 脚本会列出 apt 源中可用的内核包并按编号安装;装完可选择 `apt-mark hold` 钉死,避免被 `apt upgrade` 自动更换
release tag 格式: 内核包名格式:
```text ```text
x86_64-7.0.11 linux-image-7.0.12-bbrv3
arm64-7.0.11
``` ```
也可手动安装:`sudo apt install linux-image-7.0.12-bbrv3`
## 手动配置 apt 源(不想用脚本)
```bash
sudo install -d -m 0755 /etc/apt/keyrings
curl -fsSL https://git.chilldove.com/api/packages/icePigeon/debian/repository.key \
| sudo tee /etc/apt/keyrings/gitea-icePigeon.asc > /dev/null
echo "deb [signed-by=/etc/apt/keyrings/gitea-icePigeon.asc] https://git.chilldove.com/api/packages/icePigeon/debian stable main" \
| sudo tee /etc/apt/sources.list.d/bbrv3.list > /dev/null
sudo apt update
sudo apt install linux-image-bbrv3
```
装完按需 `sudo reboot``bbrv3-config` 写入的 cake 默认队列需重启进新内核后才生效。
## 检查 BBRv3 状态 ## 检查 BBRv3 状态
运行脚本后选择: 运行脚本后选择:
@@ -125,22 +153,25 @@ modinfo tcp_bbr 2>/dev/null | grep '^version:'
4. 卸载 BBR v3 内核 4. 卸载 BBR v3 内核
``` ```
脚本会卸载由本项目安装的 BBR v3 内核包(包名带 `-bbrv3` 后缀),删除 `/etc/sysctl.d/99-bbrv3.conf`,并更新引导配置。卸载后建议重启。 脚本会卸载由本项目安装的内核包(`linux-image-*-bbrv3` 与 meta 包 `linux-image-bbrv3`)及 `bbrv3-config`purge 时带走 `/etc/sysctl.d/99-bbrv3.conf`,并移除 apt 源 `/etc/apt/sources.list.d/bbrv3.list` 与签名公钥;引导由内核包卸载钩子自动更新。卸载后建议重启。
## 内核包来源 ## 内核包来源
`.deb` 内核包由 Gitea Actions 构建并发布到本仓库 Releases,提供 `x86_64``arm64` 两种标准 BBRv3 内核(arm64 在同一台 x86_64 runner 上交叉编译)。 `.deb` 内核包由 Gitea Actions 构建并发布到本仓库自建 Gitea 的 **Debian package registry**,提供 `amd64``arm64` 两种标准 BBRv3 内核(arm64 在同一台 x86_64 runner 上交叉编译)。一行 apt 源同时服务两种架构,apt 按本机架构自动取包。
构建流程会: 构建流程会:
- 读取 kernel.org 最新 stable 版本 - `scripts/select-stable-kernel.sh` 选定要追踪的 stable 版本(见上文「内核与 BBR 策略」)
- 下载 `gregkh/linux` 对应 stable 分支。 - 下载 `gregkh/linux` 对应 stable 分支,应用仓库内固定 BBRv3 patch
- 应用仓库内固定 BBRv3 patch - 默认启用 BBR,并把 `sch_cake` 编入内核(=y);`bbrv3-config` 包据此把默认队列设为 cake
- 默认启用 BBR,并把 `sch_cake` 编入内核(=y),安装脚本据此把默认队列设为 cake - 构建 `linux-image-bbrv3`meta,跟随最新)与 `bbrv3-config`sysctl 配置)两个辅助包
- 关闭 debug info,拒绝发布 `*-dbg*.deb` / `*-dbgsym*.deb` - 关闭 debug info,拒绝发布 `*-dbg*.deb` / `*-dbgsym*.deb`;不发布 `linux-libc-dev`(避免覆盖发行版头文件)
- 发布到 registry 的 `stable/main`,已存在的版本会跳过。
构建不会自动更新 BBR patch 本身。 构建不会自动更新 BBR patch 本身。
发布所需:CI 配置一个有 `write:package` 权限的 `PKG_TOKEN` secretGitea 实例需启用 Packages → Debian registry。
## 免责声明 ## 免责声明
内核升级有风险。安装前建议确认 VPS 控制台、救援模式或旧内核启动项可用。使用本项目构建或安装的内核造成的系统启动失败、网络异常或数据损失,由使用者自行承担。 内核升级有风险。安装前建议确认 VPS 控制台、救援模式或旧内核启动项可用。使用本项目构建或安装的内核造成的系统启动失败、网络异常或数据损失,由使用者自行承担。
@@ -0,0 +1,66 @@
# BBRv3 内核分发改造:迁移到 Gitea Debian apt 源
日期:2026-06-17
状态:已确认,进入实现
## 背景与目标
当前分发链路是「Gitea Releases 存 `.deb` + `curl|bash` 安装脚本手动 `dpkg -i`」。
目标是改成 **apt 源**,让用户 `apt upgrade` 即可时刻跟随最新 stable 内核,并顺带提升
安全性(GPG 签名、保留旧内核可回退、apt 解依赖)。
构建层(CI 每天编译 `.deb`)保留不变;本次只替换**分发/更新层**。
## 关键决策
1. **分发层 = Gitea 自带 Debian package registry**(实例已自建 Gitea,自动 PGP 签名)。
- 上传:`PUT {server}/api/packages/{owner}/debian/pool/{dist}/{comp}/upload`
- 用户源:`deb [signed-by=…] {server}/api/packages/{owner}/debian stable main`
- 公钥:`{server}/api/packages/{owner}/debian/repository.key`
- 统一用 `stable/main` 一个桶;架构由 `.deb` 自带,apt 按 `amd64/arm64` 自动取。
2. **更新策略 = 自动跟最新**:构建 meta-package `linux-image-bbrv3``Depends` 当前最新
`linux-image-<ver>-bbrv3`,版本随内核版本走。`apt upgrade` 跟最新,**旧内核保留可回退**。
谨慎用户 `apt-mark hold linux-image-bbrv3` 钉死。
3. **sysctl 配置入包**:新增 `bbrv3-config``Architecture: all`),把
`/etc/sysctl.d/99-bbrv3.conf`BBR + CAKE + ECN)作为 conffile 随包安装,`postinst`
`sysctl --system`。装包即配置,不再依赖脚本。`meta` 依赖它。
4. **迁移 = 干净切换 + 保留入口**CI 只发 registry,停发 Releases`install.sh` 仍是同一
`curl|bash` 入口,菜单不变,动作改成 apt(加源 + 导 key + 装 meta)。
5. **只发 `linux-image` / `linux-headers`,排除 `linux-libc-dev`**(避免覆盖发行版 userspace
头文件)与 `*-dbg*`
## 本次新增需求(用户补充)
6. **追踪 stable 而非 mainline**。根因:7.1.0 刚发布时 kernel.org「latest stable」== mainline
== 7.1,旧 preflight 直接取它就抓到 7.1;成熟 stable 实为 7.0.12。
**修法**`scripts/select-stable-kernel.sh``finger_banner` 选「带三段点号 `X.Y.Z`
`X.Y` 严格 `<` mainline、且仓库存在 `patches/bbrv3-linux-X.Y.patch`」中的最高版本。
三段号排除刚发布的 `7.1`,patch 闸门排除无补丁的 6.x → 当前命中 `7.0.12`。系列自动接棒。
7. **删除 joey 标签**:移除 `build.yml` 的「编译声明」步骤(注入 `Compiled & Optimized by
Joey` 的 `MODULE_DESCRIPTION`),让上游 patch 自带描述生效;Release 发布块里的 joey 文案
随该块一并删除。
## 改动清单
- `scripts/select-stable-kernel.sh`(新)— 选 stable 目标版本。
- `scripts/build-config-package.sh`(新)— 构建 `bbrv3-config_*_all.deb`。
- `scripts/build-meta-package.sh`(新)— 构建 `linux-image-bbrv3_<ver>_<arch>.deb`,依赖项
取自实际产出的内核包名(`dpkg-deb -f`),避免 localversion 后缀漂移。
- `.github/workflows/build.yml`(改)— preflight 用选择脚本 + 查 registry index 判存在;删
joey 步骤;构建 meta/config;发布改 registry `--upload-file`;停发 Release。
- `install.sh`(改)— 菜单动作改 apt:加源/导 key/装 meta(1)、装指定版本(2)、状态检查
(3,不变)、卸载并删源(4)。保留 OS 版本闸门、依赖安装、sudo 兜底。
- `README.md`(改)— 改写「内核来源/BBR 策略/菜单」,新增 apt 用法与 `apt-mark hold`。
## 前提(落地前核对)
- `git.chilldove.com` 启用 Packages → Debian registry(新版 Gitea 默认开)。
- CI 需 `PKG_TOKEN` secret(有 `write:package` scope);自动注入的 `GITHUB_TOKEN` 不保证有
包写权限。
- registry 保留历史版本以支撑「装指定版本」;将来再议 retention。
## 不做(YAGNI
- 不搭 aptly/reprepro、不自管 GPG keyGitea 全包)。
- 不做过渡期双发布。
- 不按发行版分多 distribution。
+119 -257
View File
@@ -17,23 +17,29 @@ if ! command -v sudo &> /dev/null; then
fi fi
fi fi
# 检查并安装必要依赖(命令名与 apt 包名不一致时做映射 # 检查并安装必要依赖(apt 源方式只需 curl + ca-certificates
declare -A CMD_PKG=( [curl]=curl [wget]=wget [dpkg]=dpkg [awk]=gawk [jq]=jq [sysctl]=procps ) declare -A CMD_PKG=( [curl]=curl )
APT_UPDATED=0 APT_UPDATED=0
ensure_pkg() {
local pkg="$1"
[[ "$APT_UPDATED" == "0" ]] && { sudo apt-get update > /dev/null 2>&1; APT_UPDATED=1; }
sudo apt-get install -y "$pkg" > /dev/null 2>&1
}
for cmd in "${!CMD_PKG[@]}"; do for cmd in "${!CMD_PKG[@]}"; do
if ! command -v "$cmd" &> /dev/null; then if ! command -v "$cmd" &> /dev/null; then
echo -e "\033[33m缺少依赖:$cmd,正在安装 ${CMD_PKG[$cmd]}...\033[0m" echo -e "\033[33m缺少依赖:$cmd,正在安装 ${CMD_PKG[$cmd]}...\033[0m"
[[ "$APT_UPDATED" == "0" ]] && { sudo apt-get update > /dev/null 2>&1; APT_UPDATED=1; } ensure_pkg "${CMD_PKG[$cmd]}"
sudo apt-get install -y "${CMD_PKG[$cmd]}" > /dev/null 2>&1
fi fi
done done
# 关键依赖必须就绪,否则给出明确报错(而不是后续 jq/curl 的迷惑错误) # HTTPS 根证书:apt update / 拉取签名公钥都需要
for cmd in curl wget jq; do if ! dpkg -s ca-certificates &> /dev/null; then
if ! command -v "$cmd" &> /dev/null; then echo -e "\033[33m缺少 ca-certificates,正在安装...\033[0m"
echo -e "\033[31m关键依赖 $cmd 安装失败,无法继续。请手动安装后重试。\033[0m" ensure_pkg ca-certificates
fi
if ! command -v curl &> /dev/null; then
echo -e "\033[31m关键依赖 curl 安装失败,无法继续。请手动安装后重试。\033[0m"
exit 1 exit 1
fi fi
done
# 检测系统架构 # 检测系统架构
ARCH=$(uname -m) ARCH=$(uname -m)
@@ -42,76 +48,27 @@ if [[ "$ARCH" != "aarch64" && "$ARCH" != "x86_64" ]]; then
exit 1 exit 1
fi fi
# Gitea 仓库坐标(自建实例 git.chilldove.com # Gitea 仓库 / Debian package registry 坐标(自建实例 git.chilldove.com
GITEA_HOST="https://git.chilldove.com" GITEA_HOST="https://git.chilldove.com"
GITEA_OWNER="icePigeon" GITEA_OWNER="icePigeon"
GITEA_REPO="linux-kernel-bbrv3" # Gitea 自带 Debian registryapt 源 + 自动 PGP 签名
# Gitea Releases API REG_BASE="$GITEA_HOST/api/packages/$GITEA_OWNER/debian"
RELEASES_API="$GITEA_HOST/api/v1/repos/$GITEA_OWNER/$GITEA_REPO/releases" REG_DIST="stable"
# 可选:访问私有仓库或提升 Gitea API 限额(支持 GITEA_TOKEN / GITHUB_TOKEN / GH_TOKEN REG_COMP="main"
GITEA_API_TOKEN="${GITEA_TOKEN:-${GITHUB_TOKEN:-${GH_TOKEN:-}}}" KEYRING="/etc/apt/keyrings/gitea-$GITEA_OWNER.asc"
# 安装后写入的默认网络栈配置(BBR 拥塞控制 + CAKE 队列) SOURCE_LIST="/etc/apt/sources.list.d/bbrv3.list"
# 跟随最新 stable 的 meta-packagesysctl 配置由 bbrv3-config 包负责
META_PKG="linux-image-bbrv3"
SYSCTL_DROPIN="/etc/sysctl.d/99-bbrv3.conf" SYSCTL_DROPIN="/etc/sysctl.d/99-bbrv3.conf"
# 可选:访问私有仓库时给拉取签名公钥用(支持 GITEA_TOKEN / GITHUB_TOKEN / GH_TOKEN
GITEA_API_TOKEN="${GITEA_TOKEN:-${GITHUB_TOKEN:-${GH_TOKEN:-}}}"
api_get() { # 把 uname -m 映射成 Debian 架构名(apt 用 amd64/arm64
local url="$1" get_deb_arch() {
# 不加 -fHTTP >= 400 时保留 JSON 错误体({"message":...}),交给 if [[ "$ARCH" == "aarch64" ]]; then
# check_release_api_response 解析并给出 token 提示;网络层失败仍会非零退出。 echo "arm64"
if [[ -n "$GITEA_API_TOKEN" ]]; then
curl -sSL \
-H "Authorization: token $GITEA_API_TOKEN" \
-H "Accept: application/json" \
"$url"
else else
curl -sSL "$url" echo "amd64"
fi
}
# 分页拉取全部 release(Gitea 单页上限为 50),合并成一个 JSON 数组返回
fetch_all_releases() {
local page=1 combined="[]" data count
while :; do
data=$(api_get "$RELEASES_API?limit=50&page=$page") || return 1
# 出错或非数组(例如 {"message":...})时原样返回,交给 check_release_api_response 处理
if ! echo "$data" | jq -e 'type=="array"' > /dev/null 2>&1; then
printf '%s' "$data"
return 0
fi
count=$(echo "$data" | jq 'length')
combined=$(jq -n --argjson a "$combined" --argjson b "$data" '$a + $b')
[ "$count" -lt 50 ] && break
page=$((page + 1))
[ "$page" -gt 20 ] && break
done
printf '%s' "$combined"
}
# 下载 release 资源;存在 token 时带上鉴权头以支持私有仓库
wget_asset() {
local url="$1"
if [[ -n "$GITEA_API_TOKEN" ]]; then
wget -q --show-progress --header="Authorization: token $GITEA_API_TOKEN" "$url" -P /tmp/
else
wget -q --show-progress "$url" -P /tmp/
fi
}
check_release_api_response() {
local response="$1"
local api_message=""
api_message=$(echo "$response" | jq -r 'if type=="object" then .message // "" else "" end')
if [[ -n "$api_message" ]]; then
echo -e "\033[31mGitea API 返回错误:$api_message\033[0m"
if echo "$api_message" | grep -qiE "rate limit|not found|permission|unauthor"; then
echo -e "\033[33m提示:私有仓库或受限访问可先执行 export GITEA_TOKEN=你的令牌,再重新运行脚本。\033[0m"
fi
return 1
fi
if ! echo "$response" | jq -e 'type=="array"' > /dev/null 2>&1; then
echo -e "\033[31mGitea API 返回数据格式异常,无法继续。\033[0m"
return 1
fi fi
} }
@@ -180,95 +137,36 @@ assert_supported_kernel_install_system() {
fi fi
} }
# 返回当前已安装的标准 BBRv3 内核版本(忽略遗留的 -max 包 # 函数:配置 apt 源(导入签名公钥 + 写 sources.list + apt update
get_installed_version() { setup_apt_source() {
dpkg -l 2>/dev/null \ echo -e "\033[36m正在配置 BBRv3 apt 源...\033[0m"
| awk '/^ii/ && $2 ~ /^linux-image-/ && $2 ~ /-bbrv3$/ {sub(/^linux-image-/, "", $2); print $2}' \ sudo install -d -m 0755 /etc/apt/keyrings
| sort -V | tail -n 1
}
# 把 uname -m 映射成 release tag 的架构前缀 # 拉取 Gitea registry 的签名公钥(armored,可直接用作 signed-by
get_arch_filter() { local key_args=()
if [[ "$ARCH" == "aarch64" ]]; then [[ -n "$GITEA_API_TOKEN" ]] && key_args=(-H "Authorization: token $GITEA_API_TOKEN")
echo "arm64" if ! curl -fsSL "${key_args[@]}" "$REG_BASE/repository.key" | sudo tee "$KEYRING" > /dev/null; then
elif [[ "$ARCH" == "x86_64" ]]; then echo -e "\033[31m下载仓库签名公钥失败:$REG_BASE/repository.key\033[0m"
echo "x86_64" echo -e "\033[33m提示:私有仓库可先 export GITEA_TOKEN=你的令牌 再重试。\033[0m"
fi
}
# 由 release tag(如 x86_64-7.0.12)推导已安装包应有的版本串
get_expected_installed_version() {
local tag="$1"
local version
version="${tag#x86_64-}"
version="${version#arm64-}"
echo "${version}-bbrv3"
}
# 函数:智能更新引导加载程序
update_bootloader() {
echo -e "\033[36m正在更新引导加载程序...\033[0m"
if command -v update-grub &> /dev/null; then
echo -e "\033[33m检测到 GRUB,正在执行 update-grub...\033[0m"
if sudo update-grub; then
echo -e "\033[1;32mGRUB 更新成功!\033[0m"
return 0
else
echo -e "\033[1;31mGRUB 更新失败!\033[0m"
return 1 return 1
fi fi
else if [[ ! -s "$KEYRING" ]]; then
echo -e "\033[33m未找到 'update-grub'。您的系统可能使用 U-Boot 或其他引导程序。\033[0m" echo -e "\033[31m签名公钥为空,无法继续。\033[0m"
echo -e "\033[33m在许多 ARM 系统上,内核安装包会自动处理引导更新,通常无需手动操作。\033[0m"
echo -e "\033[33m如果重启后新内核未生效,您可能需要手动更新引导配置,请参考您系统的文档。\033[0m"
return 0
fi
}
# 函数:安装后写入默认网络栈(BBR + CAKE + ECN)。内核编译期默认队列没有 cake 选项,
# 因此用 sysctl drop-in 把默认队列设为 cakesch_cake 已内置,重启进新内核后生效。
apply_default_network_stack() {
if sudo tee "$SYSCTL_DROPIN" > /dev/null <<'EOF'
# Managed by linux-kernel-bbrv3 installer
net.core.default_qdisc = cake
net.ipv4.tcp_congestion_control = bbr
net.ipv4.tcp_ecn = 1
EOF
then
echo -e "\033[1;32m✔ 已写入默认网络栈配置(BBR + CAKE + ECN):$SYSCTL_DROPIN\033[0m"
# 当前内核若已支持则立即生效;否则仅写入,重启进新内核后生效。
sudo sysctl -p "$SYSCTL_DROPIN" > /dev/null 2>&1 || true
else
echo -e "\033[33m提示:写入 $SYSCTL_DROPIN 失败,可手动设置 net.core.default_qdisc=cake。\033[0m"
fi
}
# 函数:安全地安装下载的包
install_packages() {
if ! ls /tmp/linux-*.deb &> /dev/null; then
echo -e "\033[31m错误:未在 /tmp 目录下找到内核文件,安装中止。\033[0m"
return 1 return 1
fi fi
for deb_file in /tmp/linux-*.deb; do echo "deb [signed-by=$KEYRING] $REG_BASE $REG_DIST $REG_COMP" | sudo tee "$SOURCE_LIST" > /dev/null
if ! dpkg-deb -I "$deb_file" > /dev/null 2>&1; then echo -e "\033[1;32m✔ 已写入 apt 源:$SOURCE_LIST\033[0m"
echo -e "\033[31m当前系统无法读取安装包:$deb_file\033[0m"
echo -e "\033[33m可能原因:dpkg 版本过旧,不支持该压缩格式。建议升级 dpkg 后重试。\033[0m" if ! sudo apt-get update; then
echo -e "\033[31mapt-get update 失败,请检查网络或 registry 是否可访问。\033[0m"
return 1 return 1
fi fi
done }
echo -e "\033[36m开始卸载旧版内核... \033[0m" # 函数:提示重启
INSTALLED_PACKAGES=$(dpkg -l | grep -- '-bbrv3' | awk '{print $2}' | tr '\n' ' ') prompt_reboot() {
if [[ -n "$INSTALLED_PACKAGES" ]]; then echo -n -e "\033[33m需要重启系统来加载新内核。是否立即重启? (y/N): \033[0m"
sudo apt-get remove --purge $INSTALLED_PACKAGES -y > /dev/null 2>&1
fi
echo -e "\033[36m开始安装新内核... \033[0m"
if sudo dpkg -i /tmp/linux-*.deb && update_bootloader; then
echo -e "\033[1;32m内核安装并配置完成!\033[0m"
apply_default_network_stack
echo -n -e "\033[33m需要重启系统来加载新内核。是否立即重启? (y/n): \033[0m"
read -r REBOOT_NOW read -r REBOOT_NOW
if [[ "$REBOOT_NOW" == "y" || "$REBOOT_NOW" == "Y" ]]; then if [[ "$REBOOT_NOW" == "y" || "$REBOOT_NOW" == "Y" ]]; then
echo -e "\033[36m系统即将重启...\033[0m" echo -e "\033[36m系统即将重启...\033[0m"
@@ -276,126 +174,99 @@ install_packages() {
else else
echo -e "\033[33m操作完成。请记得稍后手动重启 ('sudo reboot') 来应用新内核。\033[0m" echo -e "\033[33m操作完成。请记得稍后手动重启 ('sudo reboot') 来应用新内核。\033[0m"
fi fi
else
echo -e "\033[1;31m内核安装或引导更新失败!系统可能处于不稳定状态。请不要重启并寻求手动修复!\033[0m"
fi
} }
# 函数:检查并安装最新版本 # 函数:安装或更新到最新版(meta-package 自动跟随最新 stable
install_latest_version() { install_latest_version() {
local arch_filter
local expected_version
assert_supported_kernel_install_system || return 1 assert_supported_kernel_install_system || return 1
setup_apt_source || return 1
echo -e "\033[36m正在从 Gitea 获取最新版本信息...\033[0m" echo -e "\033[36m正在安装/升级 $META_PKG(自动跟随最新 stable,旧内核会保留以便回退)...\033[0m"
RELEASE_DATA=$(fetch_all_releases) # 内核包的 postinst 会自动更新引导(update-grub / kernel-install),无需手动处理;
if [[ -z "$RELEASE_DATA" ]]; then # bbrv3-config 的 postinst 会写入并应用 BBR + CAKE + ECN 的 sysctl 配置。
echo -e "\033[31m从 Gitea 获取版本信息失败。请检查网络连接或 API 状态。\033[0m" if sudo apt-get install -y "$META_PKG"; then
echo -e "\033[1;32m内核安装并配置完成!默认网络栈:BBR + CAKE + ECN。\033[0m"
echo -e "\033[33m如不希望以后 apt upgrade 自动更换内核,可执行:sudo apt-mark hold $META_PKG\033[0m"
prompt_reboot
else
echo -e "\033[1;31m内核安装失败!系统未变更内核,可重试或检查上面的 apt 报错。\033[0m"
return 1 return 1
fi fi
check_release_api_response "$RELEASE_DATA" || return 1
arch_filter=$(get_arch_filter)
LATEST_TAG_NAME=$(echo "$RELEASE_DATA" | jq -r --arg filter "$arch_filter" '
map(
select(.tag_name | test("^" + $filter + "-[0-9]"; "i"))
| select(.tag_name | endswith("-max") | not)
)
| sort_by(.published_at)
| .[-1].tag_name
')
if [[ -z "$LATEST_TAG_NAME" || "$LATEST_TAG_NAME" == "null" ]]; then
echo -e "\033[31m未找到适合当前架构 ($ARCH) 的最新版本。\033[0m"
return 1
fi
echo -e "\033[36m检测到最新版本:\033[0m\033[1;32m$LATEST_TAG_NAME\033[0m"
INSTALLED_VERSION=$(get_installed_version)
echo -e "\033[36m当前已安装版本:\033[0m\033[1;32m${INSTALLED_VERSION:-"未安装"}\033[0m"
expected_version=$(get_expected_installed_version "$LATEST_TAG_NAME")
if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" == "$expected_version" ]]; then
echo -e "\033[1;32m(o'▽'o) 您已安装最新 BBR v3 内核,无需更新!\033[0m"
return 0
fi
echo -e "\033[33m发现新版本或未安装内核,准备下载...\033[0m"
ASSET_URLS=$(echo "$RELEASE_DATA" | jq -r --arg tag "$LATEST_TAG_NAME" '
.[] | select(.tag_name == $tag) | .assets[].browser_download_url
| select(test("(-dbg_|-dbgsym_)"; "i") | not)
')
rm -f /tmp/linux-*.deb
for URL in $ASSET_URLS; do
echo -e "\033[36m正在下载文件:$URL\033[0m"
wget_asset "$URL" || { echo -e "\033[31m下载失败:$URL\033[0m"; return 1; }
done
install_packages
} }
# 函数:安装指定版本 # 函数:安装指定版本(从 apt 源里选一个具体的内核版本)
install_specific_version() { install_specific_version() {
local arch_filter
assert_supported_kernel_install_system || return 1 assert_supported_kernel_install_system || return 1
setup_apt_source || return 1
RELEASE_DATA=$(fetch_all_releases) echo -e "\033[36m正在从 apt 源获取可用版本...\033[0m"
if [[ -z "$RELEASE_DATA" ]]; then # 列出仓库中所有带版本号的 BBRv3 内核包(排除无版本号的 meta 包本身)
echo -e "\033[31m从 Gitea 获取版本信息失败。请检查网络连接或 API 状态。\033[0m" mapfile -t PKGS < <(apt-cache pkgnames 2>/dev/null \
return 1 | grep -E '^linux-image-[0-9].+-bbrv3$' | sort -V)
fi
check_release_api_response "$RELEASE_DATA" || return 1
arch_filter=$(get_arch_filter) if [[ ${#PKGS[@]} -eq 0 ]]; then
MATCH_TAGS=$(echo "$RELEASE_DATA" | jq -r --arg filter "$arch_filter" ' echo -e "\033[31m仓库中未找到可用的 BBRv3 内核版本。\033[0m"
.[]
| select(.tag_name | test("^" + $filter + "-[0-9]"; "i"))
| select(.tag_name | endswith("-max") | not)
| .tag_name
')
if [[ -z "$MATCH_TAGS" ]]; then
echo -e "\033[31m未找到适合当前架构的可用版本。\033[0m"
return 1 return 1
fi fi
echo -e "\033[36m以下为适用于当前架构的可用版本:\033[0m" echo -e "\033[36m以下为可用的内核版本:\033[0m"
IFS=$'\n' read -rd '' -a TAG_ARRAY <<<"$MATCH_TAGS" for i in "${!PKGS[@]}"; do
echo -e "\033[33m $((i+1)). ${PKGS[$i]}\033[0m"
for i in "${!TAG_ARRAY[@]}"; do
echo -e "\033[33m $((i+1)). ${TAG_ARRAY[$i]}\033[0m"
done done
echo -n -e "\033[36m请输入要安装的版本编号(例如 1):\033[0m" echo -n -e "\033[36m请输入要安装的版本编号(例如 1):\033[0m"
read -r CHOICE read -r CHOICE
if ! [[ "$CHOICE" =~ ^[0-9]+$ ]] || (( CHOICE < 1 || CHOICE > ${#PKGS[@]} )); then
if ! [[ "$CHOICE" =~ ^[0-9]+$ ]] || (( CHOICE < 1 || CHOICE > ${#TAG_ARRAY[@]} )); then
echo -e "\033[31m输入无效编号,取消操作。\033[0m" echo -e "\033[31m输入无效编号,取消操作。\033[0m"
return 1 return 1
fi fi
INDEX=$((CHOICE-1)) local pkg="${PKGS[$((CHOICE-1))]}"
SELECTED_TAG="${TAG_ARRAY[$INDEX]}" echo -e "\033[36m已选择:\033[0m\033[1;32m$pkg\033[0m"
echo -e "\033[36m已选择版本:\033[0m\033[1;32m$SELECTED_TAG\033[0m"
ASSET_URLS=$(echo "$RELEASE_DATA" | jq -r --arg tag "$SELECTED_TAG" ' if sudo apt-get install -y "$pkg"; then
.[] | select(.tag_name == $tag) | .assets[].browser_download_url echo -e "\033[1;32m已安装 $pkg。\033[0m"
| select(test("(-dbg_|-dbgsym_)"; "i") | not) # 装指定版本通常是想钉死,提示如何阻止 apt 自动更换内核
') if dpkg -s "$META_PKG" &> /dev/null; then
echo -n -e "\033[33m检测到已安装 $META_PKG,是否钉死当前内核、阻止 apt upgrade 自动更换? (y/N): \033[0m"
read -r HOLD_NOW
if [[ "$HOLD_NOW" == "y" || "$HOLD_NOW" == "Y" ]]; then
sudo apt-mark hold "$META_PKG" "$pkg"
echo -e "\033[1;32m已钉死:$META_PKG $pkg(解钉:sudo apt-mark unhold ...\033[0m"
fi
fi
prompt_reboot
else
echo -e "\033[1;31m安装 $pkg 失败!请检查上面的 apt 报错。\033[0m"
return 1
fi
}
rm -f /tmp/linux-*.deb # 函数:卸载本项目安装的所有内容(内核包 + meta + config),并移除 apt 源
uninstall_bbrv3() {
# 带 -bbrv3 后缀的包既包含具体内核(linux-image-7.0.x-bbrv3)也包含 metalinux-image-bbrv3
local pkgs
pkgs=$(dpkg -l 2>/dev/null | awk '/^ii/ && $2 ~ /-bbrv3$/ {print $2}' | tr '\n' ' ')
pkgs+=" $(dpkg -s bbrv3-config &> /dev/null && echo bbrv3-config)"
pkgs=$(echo "$pkgs" | xargs) # 规整空白
for URL in $ASSET_URLS; do if [[ -n "$pkgs" ]]; then
echo -e "\033[36m下载中:$URL\033[0m" echo -e "\033[36m将要卸载以下包:\033[33m$pkgs\033[0m"
wget_asset "$URL" || { echo -e "\033[31m下载失败:$URL\033[0m"; return 1; } # 内核包 postrm 钩子会自动更新引导;purge 会带走 99-bbrv3.confconffile
done sudo apt-get remove --purge -y $pkgs
else
echo -e "\033[33m未找到由本项目安装的 BBRv3 内核包。\033[0m"
fi
install_packages # 移除 apt 源与签名公钥
if [[ -f "$SOURCE_LIST" || -f "$KEYRING" ]]; then
sudo rm -f "$SOURCE_LIST" "$KEYRING"
echo -e "\033[36m已移除 apt 源与签名公钥。\033[0m"
sudo apt-get update > /dev/null 2>&1 || true
fi
# 兜底清理可能残留的 sysctl drop-in(旧版脚本写入、或非 conffile 情形)
sudo rm -f "$SYSCTL_DROPIN"
echo -e "\033[1;32m卸载完成。请记得重启系统。\033[0m"
} }
# 美化输出的分隔线 # 美化输出的分隔线
@@ -411,7 +282,7 @@ echo -e "\033[1;35m(☆ω☆)✧*。 欢迎使用 BBR v3 内核安装脚本 ✧*
print_separator print_separator
echo -e "\033[1;33m╭( ・ㅂ・)و ✧ 你可以选择以下操作哦:\033[0m" echo -e "\033[1;33m╭( ・ㅂ・)و ✧ 你可以选择以下操作哦:\033[0m"
echo -e "\033[33m 1. 🚀 安装或更新 BBR v3 内核(最新版)\033[0m" echo -e "\033[33m 1. 🚀 安装或更新 BBR v3 内核(最新版apt 源自动跟随\033[0m"
echo -e "\033[33m 2. 📚 安装指定版本\033[0m" echo -e "\033[33m 2. 📚 安装指定版本\033[0m"
echo -e "\033[33m 3. 🔍 检查 BBR v3 状态\033[0m" echo -e "\033[33m 3. 🔍 检查 BBR v3 状态\033[0m"
echo -e "\033[33m 4. 🗑️ 卸载 BBR v3 内核\033[0m" echo -e "\033[33m 4. 🗑️ 卸载 BBR v3 内核\033[0m"
@@ -469,16 +340,7 @@ case "$ACTION" in
;; ;;
4) 4)
echo -e "\033[1;32mヽ(・∀・) 您选择了卸载 BBR v3 内核!\033[0m" echo -e "\033[1;32mヽ(・∀・) 您选择了卸载 BBR v3 内核!\033[0m"
PACKAGES_TO_REMOVE=$(dpkg -l | grep -- '-bbrv3' | awk '{print $2}' | tr '\n' ' ') uninstall_bbrv3
if [[ -n "$PACKAGES_TO_REMOVE" ]]; then
echo -e "\033[36m将要卸载以下内核包: \033[33m$PACKAGES_TO_REMOVE\033[0m"
sudo apt-get remove --purge $PACKAGES_TO_REMOVE -y
sudo rm -f "$SYSCTL_DROPIN"
update_bootloader
echo -e "\033[1;32m内核包已卸载。请记得重启系统。\033[0m"
else
echo -e "\033[33m未找到由本脚本安装的 BBR v3 内核包。\033[0m"
fi
;; ;;
*) *)
echo -e "\033[31m( ̄▽ ̄)ゞ 无效的选项,请输入 1-4 之间的数字哦~\033[0m" echo -e "\033[31m( ̄▽ ̄)ゞ 无效的选项,请输入 1-4 之间的数字哦~\033[0m"
+54
View File
@@ -0,0 +1,54 @@
#!/usr/bin/env bash
# Build the bbrv3-config package (Architecture: all): ships the BBR + CAKE + ECN
# sysctl drop-in so installing the kernel configures the network stack with no
# installer script. The meta-package depends on it.
set -euo pipefail
out_dir=${1:?usage: build-config-package.sh <output_dir> [version]}
version=${2:-1.0}
work=$(mktemp -d)
trap 'rm -rf "$work"' EXIT
pkgdir="$work/bbrv3-config"
mkdir -p "$pkgdir/DEBIAN" "$pkgdir/etc/sysctl.d"
cat > "$pkgdir/etc/sysctl.d/99-bbrv3.conf" <<'EOF'
# Managed by the bbrv3-config package (linux-kernel-bbrv3).
# BBR congestion control + CAKE default qdisc + ECN.
net.core.default_qdisc = cake
net.ipv4.tcp_congestion_control = bbr
net.ipv4.tcp_ecn = 1
EOF
# conffile: dpkg preserves local edits to the drop-in across upgrades.
echo "/etc/sysctl.d/99-bbrv3.conf" > "$pkgdir/DEBIAN/conffiles"
cat > "$pkgdir/DEBIAN/control" <<EOF
Package: bbrv3-config
Version: $version
Architecture: all
Maintainer: linux-kernel-bbrv3
Depends: procps
Section: net
Priority: optional
Description: Default network stack (BBR + CAKE + ECN) for BBRv3 kernels
Ships /etc/sysctl.d/99-bbrv3.conf so the BBRv3 kernel boots with BBR
congestion control, the CAKE default qdisc and ECN enabled.
EOF
cat > "$pkgdir/DEBIAN/postinst" <<'EOF'
#!/bin/sh
set -e
if [ "$1" = "configure" ]; then
# Applies now if the running kernel supports it; otherwise the settings take
# effect on the next boot into the BBRv3 kernel (sch_cake is built in there).
sysctl --system >/dev/null 2>&1 || true
fi
EOF
chmod 0755 "$pkgdir/DEBIAN/postinst"
mkdir -p "$out_dir"
dpkg-deb --build --root-owner-group "$pkgdir" \
"$out_dir/bbrv3-config_${version}_all.deb"
echo "Built $out_dir/bbrv3-config_${version}_all.deb"
+38
View File
@@ -0,0 +1,38 @@
#!/usr/bin/env bash
# Build the linux-image-bbrv3 meta-package. It depends on a specific built kernel
# package plus bbrv3-config, so `apt upgrade` tracks the newest BBRv3 kernel while
# previously installed kernels stay around for fallback.
#
# The dependency is the *actual* produced package name (passed in), not a guessed
# one, so any localversion/suffix drift from the kernel build can't break it.
set -euo pipefail
meta_version=${1:?usage: build-meta-package.sh <meta_version> <deb_arch> <depends_pkg> <out_dir>}
arch=${2:?missing deb arch (amd64|arm64)}
depends_pkg=${3:?missing kernel image package name to depend on}
out_dir=${4:?missing output dir}
work=$(mktemp -d)
trap 'rm -rf "$work"' EXIT
pkgdir="$work/meta"
mkdir -p "$pkgdir/DEBIAN"
cat > "$pkgdir/DEBIAN/control" <<EOF
Package: linux-image-bbrv3
Version: $meta_version
Architecture: $arch
Maintainer: linux-kernel-bbrv3
Depends: $depends_pkg, bbrv3-config
Section: kernel
Priority: optional
Description: BBRv3 kernel (tracks the latest stable build)
Meta-package pulling in the newest BBRv3 kernel built by this project
($depends_pkg) plus the bbrv3-config network defaults. Run
'apt-mark hold linux-image-bbrv3' to pin your kernel and stop auto-tracking.
EOF
mkdir -p "$out_dir"
dpkg-deb --build --root-owner-group "$pkgdir" \
"$out_dir/linux-image-bbrv3_${meta_version}_${arch}.deb"
echo "Built $out_dir/linux-image-bbrv3_${meta_version}_${arch}.deb"
+60
View File
@@ -0,0 +1,60 @@
#!/usr/bin/env bash
# Print the kernel version this project should build.
#
# We track an established *stable* series, never the bleeding edge. Right after a
# new series N.M is released, kernel.org's "latest stable version" flips to "N.M"
# and equals "latest mainline version" — that is mainline, not a baked stable.
# So we select the highest three-component point release (X.Y.Z) whose series X.Y
# is strictly below mainline AND has a matching BBRv3 patch in this repo:
# - three components exclude the freshly-cut top series (shown as "N.M");
# - the "< mainline" test excludes a series that still equals mainline;
# - the patch gate excludes series we cannot port BBRv3 onto (e.g. LTS 6.x).
# The series advances automatically once the next one matures and mainline moves on.
set -euo pipefail
script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
repo_root=$(cd -- "$script_dir/.." && pwd)
FINGER_URL="${FINGER_URL:-https://www.kernel.org/finger_banner}"
banner=$(curl -fsSL "$FINGER_URL")
if [ -z "$banner" ]; then
echo "Failed to fetch $FINGER_URL" >&2
exit 1
fi
# Mainline major.minor, stripping any -rcN suffix: "7.1" or "7.2-rc1" -> "7.1"/"7.2".
mainline_mm=$(printf '%s\n' "$banner" \
| grep -i 'latest mainline version' \
| grep -oE '[0-9]+\.[0-9]+' | head -n1)
if [ -z "$mainline_mm" ]; then
echo "Could not parse mainline version from finger_banner." >&2
exit 1
fi
# Every three-component point release mentioned in the banner (stable + longterm),
# ascending and de-duplicated. Two-component entries like a just-released "7.1"
# are intentionally not matched here.
mapfile -t candidates < <(printf '%s\n' "$banner" \
| grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | sort -V -u)
# strictly_less A B -> true when A < B under version sort.
strictly_less() {
[ "$1" != "$2" ] && [ "$(printf '%s\n%s\n' "$1" "$2" | sort -V | head -n1)" = "$1" ]
}
target=""
for v in "${candidates[@]}"; do
mm=${v%.*} # 7.0.12 -> 7.0
strictly_less "$mm" "$mainline_mm" || continue
[ -f "$repo_root/patches/bbrv3-linux-$mm.patch" ] || continue
target="$v" # candidates ascending: keep highest match
done
if [ -z "$target" ]; then
echo "No stable series below mainline $mainline_mm has a BBRv3 patch in patches/." >&2
echo "Point releases seen: ${candidates[*]:-none}" >&2
exit 1
fi
printf '%s\n' "$target"