Skip to content

LankeOS v0.21

Latest

Choose a tag to compare

@Wtada233 Wtada233 released this 06 Oct 09:49

LankeOS v0.21 Release

Codename: Irrawaddy

“And believe me I am still alive.”

“I'm doing maintenance and I'm still alive...”


核心变更 (Major Changes)

0.21 的主题是 “把判据收成一份,把界面收成一层”。这个版本没有新包、也没有上游版本跟进——861 个包里唯一一个版本号变了的包就是 lpkg 自己(另外 linux 与 filesystem 各 bump 了一次 release)。全部工程量都压在包管理器上:lpkg 从 7.1.1 一路推到 8.0.0,中间还顺手换了一整套终端界面。

全新终端界面:pacman 风格

安装的输出以前是一串各说各话的日志行——正在解压到临时目录...、正在解压文件: 300 个已处理、解压完成,共处理 300 个文件、正在复制文件到系统...——同一个包刷七八行,一次 300 包的升级能刷出约 900 行,而且每行都不说在装谁。现在拆成两层,一眼可分:

  • :: <阶段> 分隔条(pacman 风格)::: 下载 → :: 解压 → :: 安装 / :: 升级 → :: 运行安装后钩子 → :: 系统触发器 → :: 已安装 N 个包: …(卸载侧是 :: 卸载 → :: 系统触发器 → :: 已卸载 N 个包: …)。
  • ==> <动作> ... [OK] 单行动作行(openrc风格..?我以前用 gentoo 的时候有一点印象,大概是吧。[OK] / [FAILED] / [SKIPPED] 贴终端最右):每个阶段只有一行,长操作期间先打出"在干什么",右侧原地刷新。
  • 进度条:[####------] 45%,下载条带字节数 + 速率 + ETA(mm:ss,pacman 的写法),解压条按字节、安装条按文件数、git 源按对象数。
  • 批次末一条 summary:逐包的"X 已成功安装!"全部删除,300 包的升级从 ~900 行降到 ~300 行 + 1 行。
  • 非 TTY 一律降级为纯文本:字面意思,我懒得解释了,重定向输出可见。

与之配套的一批"人机界面"修正(都不只是好看,脚本/farm 靠它们分辨成败):

  • 取消 ≠ 完成:交互提示答"不"、验证码输错、Ctrl+C 统一抛 UserAbort——打消息(不套 Error: 前缀,用户取消不是错误)+ 退出码 1,且不再打"安装/卸载完成"、不再打 summary。(此前 install/upgrade/remove 三条取消路径会继续走到完成行、退出码 0;remove -r 输错验证码就是这个症状。)
  • 交互输入期间 Ctrl+C 真的生效:所有交互输入改走可中断的 read_line_interruptible()——裸 std::cin >> x 在 glibc 的 SA_RESTART 下会被自动重启,Ctrl+C 只置标志、进程仍卡着(用户看到的是"Ctrl+C 无效,只能 kill -9")。
  • --help 走 stdout(lpkg --help | less 此前拿到的是空——全写了 stderr);参数不合法打用法仍走 stderr。
  • 命令拼错不再报"需要 root":未知命令现在在要 root、加锁、跑恢复之前就被拒(打用法 + 退 1)。此前打错一个字母,非 root 用户看到的是 "Root permission is required"——完全指错方向。
  • query <未安装> / query <无主文件> 退出码改 1(此前退 0,且复用 remove 语境的文案);remove 只在真删了东西时才打"卸载完成"。
  • install 的本地包参数解析不出来时中止,不再"打个错误、丢掉这个参数、然后报成功退 0";同理,请求的目标没能进计划也是硬错(error.target_missing_from_plan)。
  • upgrade 在索引解析出 0 个包时不再假装"已是最新":本机装着包而索引是空的,现在抛错而不是打"所有包都已更新"再退 0(远程索引/空索引以前看起来跟成功一模一样)。
  • 同一原则贯彻在所有路径上:任何地方都不许把异常吞成"打条警告就正常返回"——那等于告诉脚本"做成了"。

安装流水线重构(功能性,不只是版式)

  • :: 下载 阶段就做逐字段的元数据核对:归档的 metadata.json 与索引逐字段比对(name / version / deps / provides / provides_soname / needed_so),任何一项不符即抛错——此刻什么都还没落盘(连 BEGIN_PKGS 都还没写)。以前这一步在事务内部做,不一致时是"重解析依赖、重置游标、继续";现在整批回滚并硬报错,且明确不再加回"动态重解析"。
  • 文件冲突预检提到整批之前:check_batch_file_conflicts 拿全批成员的清单 + 当前归属 + 批内接管顺序一次性判完,跑在事务开始前。以前是逐包判,第一个包的 prerm 钩子可能已经跑过、然后整批回滚。
  • 每个批次只写 2 次 DB(:batch-start / :batch-end),不再逐包 flush。
  • 安装后钩子成为独立阶段(仍在提交之后、不在 WAL 里——这是有意的),钩子目录清理改用不抛异常的增量循环,免得把已经提交的批次搞失败。
  • 移除侧的两条行为修正:remove -r / autoremove 曾硬编 force=true、完全不处理 /etc,会直接删掉配置文件——现在每条移除路径都把配置改名成 <path>.lpkgsave(只有 --purge-config 才真删);remove a b(b 依赖 a)以前会被整体拒绝("a 还被 b 需要"),现在批内成员不再互相阻挡。
  • --overwrite 的模式侧补剥尾斜杠(usr/lib/foo/ 此前静默不匹配),但剥完只剩通配符的不剥(*/ 保持不匹配 = fail-closed,免得把"只豁免目录"悄悄放宽成"豁免一切")。
  • lpkg scan 重写:扫描根从 {usr,etc,opt,var} 改成整个根,忽略集补上 /var、/mnt、/home、/root 且不再下钻(以前只是"报告时过滤")。旧实现在扫 / 时会走进 /proc 然后静默截断——一边报着"已扫描 389551 个文件"、一边 /usr 下一个文件都没扫到。现在截断会明说(warning.scan_walk_aborted)。
  • depend remove 现在也跟 deps 边(此前只看 SONAME 边,会少报受影响集合);lpkg depend / scan 的树状输出此前是硬编英文,现已接入本地化。

lpkg 8.0.0:两处破坏性改动

两处都不兼容:

  • provides 拆成两个字段。旧的一列同时装着"虚拟能力"和"本包导出的 SONAME",分类只能靠字符串形状猜(looks_like_soname),由此生出三条实测过的毛病:求解器里两个命名空间互相串、farm 全量覆写 provides 会把手写的虚拟提供顶掉(实测真实仓库 2748 条 provides 全部含 .so,虚拟提供 0 条)、于是"虚拟包"这条语义只活在测试里。现在池里只剩两个命名空间,全程零形状判断:

    deps: X / 能力型 target   →  裸名 X   →  包名(自提供)或虚拟 provides
    needed_so: X              →  so:X     →  只有 provides_soname 里的 X
    

    so: 前缀含 :,而包名不允许含 :,所以这两者结构上不可能再出现离奇冲突。索引版本块随之收紧为恰好 6 段(ver:hash:deps:provides:provides_soname:needed_so),新增 provides_soname.db(DB 一族 6→7 个)。

  • 版本语义直接采用 rpm 的 EVR。旧设计有两套语义(lpkg 自己的 - = 预发布 / +N = 发行修订号,加一层 to_libsolv_evr() 编解码桥),两套判据在 4032 组合的(候选 × 约束)矩阵上差过 78 处,两个方向都有——假满足会装出坏系统,假不满足则事务无解。期间先用"换更冷门的编码(^ 当 release 分隔符)"救过一轮,但那只治标:只要还存在第二套语义,就得靠手动保持一致活着。8.0.0 把桥接整段删掉了。

  • 成品版本串随之由 1.2.3+1 变成 1.2.3-1(807 个带非零 release 的包受影响,纯文本迁移)。

  • 预发布改用 ~:1.0~rc1 < 1.0;而 -rc1 现在是 release,1.0-rc1 > 1.0。

  • 顺带修掉一处既有误判:ncurses=6.5-20250809 是上游的补丁快照(更新),旧语义把它判成旧于 6.5,新语义下它是 6.5 的 release。

ABI 审计进入「符号版本」级

以前只按裸 SONAME 匹配。glibc 升版新增 GLIBC_2.40、libstdc++ 的 GLIBCXX_3.4.x、Qt 的私有 API——这些都是同一个 SONAME 内部的 ABI 细节,"二进制要求某个符号版本、提供者拿不出来"过去完全看不见,只能等运行期崩。

  • 语法:libc.so.6(裸)、libc.so.6@GLIBC_2.40(单个)、libc.so.6@{GLIBC_2.40,GLIBC_2.39}(多个,只能在末尾、只出现一次)。
  • 匹配是保守的:带符号版本的需求只能被"也声明了符号版本、且覆盖它"的提供者满足——只写裸 libc.so.6 的提供者不算数(否则这个特性形同虚设)。裸需求不受影响:声明得更细不会打破裸需求。
  • farm 自动产出:每个 ELF 的 .gnu.version_d(verdef)与 .gnu.version_r(verneed)被扫出来写进这两个字段,不需要手编。实测产物:glibc 的 libc.so.6 声明了 46 个版本节点,gcc 的 libgcc_s.so.1@{GCC_3.0,…,GCC_7.0.0},qt6-base 的 libQt6Core.so.6@{NonQt,Qt_6,…,Qt_6_PRIVATE_API}。
  • 全仓 861 个包一次性回填:逐包解包 → 扫 ELF → 写回配方 → 按同一份扫描结果重打归档,并做了全量独立复算与三方一致性验收。
  • C++ (so_spec.cpp) 与 Python (check_deps.py) / Rust (scan.rs) 三方切分规则同一份,由同一组真值表双向钉住。

引入 fuzzing,然后它抓到了三个真缺陷

第一次给 lpkg 上 libFuzzer(make fuzz,ASan + UBSan,11 个 harness:ELF strip、strip 整归档、归档成员名、版本比较差分、SONAME 目录、WAL 行、DB 行、依赖串、索引行、求解器差分、get_elf_soname)。它上线当天就在 strip.cpp 的 ET_REL 路径上抓到三处畸形输入漏洞:

  1. e_shoff 没有下界 ⇒ e_shoff == 0 时写节区头正好覆盖 ELF 头,函数返回 true,损坏内容被写回原文件、还报了成功。
  2. sh_offset/sh_size 没有量级封顶 ⇒ 一个 1224 字节的畸形 ET_REL 能声明约 480 GB 的节区,elf_update 照着去分配。
  3. sh_addralign 没有量级封顶 ⇒ 一个 sh_addralign = 2^56 的节区让输出偏移跳到 64 PiB,memfd 被 ftruncate 到 64 PiB 并真实占掉几十 GB。实测:宿主可用内存 7 秒掉 48 GB,而进程自身 RSS 只有 0.4 GB。这正是之前几次"莫名其妙打崩桌面"的元凶——最小复现是"取一个正常 .o,只把这一个字段改成 2^32",读代码几乎不可能发现。

多轮并行审计的收口(安全与正确性)

0.20 之后到 0.21 之间跑了多轮并行只读审计(五路 / 四路,按子系统切:装卸入口、安装趟与文件操作、ELF 与归档、WAL 与恢复、求解器与配置,外加定位与测试可信度专项):

  • 暂存目录改为 mkdtemp(原子、0700):此前是 exists() 探测 + create_directories(),TOCTOU——本地无权用户预建同名目录或符号链接就能劫持 root 的暂存目录。
  • --root 下的祖先链穿透:落位目标只解析父目录、且新增整条路径级判据,包内合法的绝对符号链接照旧放行,但从中间段逃出 root 不可能了。写入层 8 个原语各自入口加了同一道闸(DB 键派生的路径从此与归档内容那条腿同源)。
  • DIR_META 字段改严格十进制解析:stoul("-1") 不抛、回绕成 07777(setuid + setgid + sticky + 世界可写)——一条写坏的 WAL 行就能把任意目录改成完全开放。
  • 恢复期的 post-commit 清理加前缀守卫:此前按 WAL 里的字面路径 remove_all,一条被篡改的 CLEANUP /etc 能删掉整棵 /etc。
  • WAL 回滚的 DB 备份判定改 lstat 语义:悬空链接会让回滚静默跳过还原,备份随后被清理。
  • 归档成员名分帧字符:deps/provides/needed_so 拒控制字符(\n 会读回成额外依赖、\t 会截断 provides.db 的键),包名/版本额外拒 ,/|/;(coreutils,evil 会让 files.db 的属主集合读回成两个幽灵属主);归档里的特殊文件(FIFO / 字符 / 块设备)整包拒绝。
  • 新增 archive/tar_guard.{hpp,cpp}:重名成员 / 控制字符 / 超长名字 / .. 分量 / 符号链接目标逃出根,五条判据一律整包拒绝。
  • 静态库(.a)符号索引重算:strip 让 .o 变短后,照抄的 ar 索引偏移就失效——本机实测有 4 个真实 .a 被改坏(ld 直接报 no more archived files)。现在按 GNU ar 自己的做法重算索引(占位 + 原地回填),认不出的索引(BSD __.SYMDEF / 畸形 GNU 索引)则整份归档不剥 + 告警——宁可少剥一个库,也不写一份偏移可能错的索引。
  • 反向依赖的键提取收成一份(dependency_name_of()):此前某处按空白切分,于是 provb>=2.0 变成一个查不到的键——autoremove 有可能删掉一个仍被需要的包。移除侧的状态读取同时改成 fail-closed(FIFO / 不可读 / 目录都会报错并回滚,以前会被读成"没有反向依赖",留下永久的陈旧边)。
  • content/ 根必须是真目录、落位暂存路径拒符号链接(error.tmp_path_is_symlink)——构造过的归档不能把包内容指向解压根之外,也不能让暂存写入穿过链接改到目标上。
  • WAL 未配对批次守卫:WAL 里还有未完成批次时拒绝开新批次;且此时 cleanup_db_backups() 提前返回(lstat 语义),不把唯一的还原点删掉。
  • SIGINT 检查点铺到进程内的长步骤(curl 进度回调、strip 循环、打包前):此前只在阶段边界检查,下载 / strip / 打包窗口里的 Ctrl+C 被静默吞掉;build / pack 此前更是直接 SIG_DFL,中断后会留下 build/work|content|hooks 残骸。

结构重构

  • base/utils.hpp 拆成 11 个子头(process / locking / tmpdir / path_predicates / path_safety / stash / fs_atomic / io_set_file / repo_index / strings / xattr),utils.hpp 变成只做转发的聚合头——约 90 个 include 方零改动。
  • 新增 repo/revdep.{hpp,cpp}:反向依赖图收成一份实现,按边种类参数化(SonameOnly / DepsAndSoname),替换原先两份各自演化、会漂移的实现。
  • db/* 收口:DB 里程碑坍缩成每批次两个(:batch-start / :batch-end),批次配对与扫描式恢复、备份 GC 守卫、op-sink 与 WAL 语义统一。
  • 工具链:删除 build_deps_summary.sh(636 行);check_deps.py 扩展成 SONAME / provides_soname 感知;新增 check_index_conformance.py(C++ 与 Python 的索引格式契约,make check-index-format)。
  • Makefile:新增 check-index-format / compile-commands / tidy / test-sanitize / fuzz / fuzz-collect 目标;构建依赖去掉 fmt-dev(改用自带的 string_format)、加入 clang / compiler-rt / clang-extra-tools(sanitizer 运行时与 clang-tidy);format-check 覆盖 165 → 197 个文件。
  • CI 接上了 make format-check:这个检查在 CI 里一直没接线(容器是 alpine、没装 clang,必须跑在宿主)。现固定在 ubuntu-24.04 + apt.llvm.org 的官方 clang-format-23——版本必须钉在 23.x,clang-format 换大版本会因换行决策不同而误报。

构建农场(lankefarm)

  • 适配lpkg的新动态库符号版本解析代码 生成lpkg可识别的metadata。
  • 版本比较器换成 libsolv rpm 算法的忠实移植(track/vercmp.rs),以 3944 对期望值(由容器里的真 libsolv 生成)逐值对齐兜底——那是"移植是否忠实"的唯一证据链。

打包修复

  • filesystem:打包时 chown -R root:root(此前 staging 树里的属主是1000)、清掉源里的 .gitkeep 占位文件,并补上 /var/lock → ../run/lock、/var/run → ../run 两个符号链接。
  • linux:打开 CONFIG_MEMCG。

迁移指南

  • 0.20 → 0.21 不是 lpkg upgrade 一条命令能完成的。索引版本块从 5 段变 6 段、provides 拆成两个字段,7.x 的 lpkg 读不了 8.0.0 的仓库,8.0.0 也读不了 7.x 装出来的 DB。两条路二选一:
    1. 推荐:下载新 ISO 重装或就地替换(干净、无残留)。
    2. 就地迁移:解压 lankeos_final_0.21_pkgs.tar.xz 与 lankeos_final_0.21_lpkg_merge.tar.xz,然后
      sudo ./lpkg-merge --dry-run --pkgs-dir /path/to/pkgs   # 先只看计划
      sudo ./lpkg-merge --pkgs-dir /path/to/pkgs             # 确认后执行
      它会用你现在的 lpkg 把 lpkg 本体重装成 8.0.0,之后每个包都用新 lpkg 重装一遍——这是让新语义重建数据库最直接的办法。注意 reinstall 必须带 --no-deps(迁移工具已经替你带了;手工重装时忘了会撞 unresolved_soname)。
  • 预发布写法变了:以前写 1.0-rc1 表示预发布,现在 -rc1 是 release(比 1.0 新)。要表达预发布请写 1.0~rc1。如果你自己写的依赖约束里用了 -rc1/-beta 这类后缀,升级前先改过来。
  • lpkg install <SONAME> 不再解析 SONAME:install 不做任何 soname 特判,找不到就按"未找到包"报错。要按能力安装请用虚拟 provides。
  • 虚拟 provides 不能用 so: 前缀:那是求解器内部的 SONAME 命名空间,写了会被元数据校验直接拒绝(报错会点名是哪一段)。
  • 包的成品版本号形态变了(1.2.3+1 → 1.2.3-1),如果你有脚本在解析版本串,注意这个分隔符。
  • 给脚本作者的提醒:本版把"取消"和"失败"都收敛成非零退出码(以前取消/部分失败可能退 0)。如果你的脚本只看退出码,这版之后它的判断才是对的;反之,若你此前依赖"退 0"来判断某种状态,请重新核对。

镜像体积变化

  • 完整 LiveISO 1.40 GB(1.30 GiB)—— 与 0.20 的 1.40 GB 几乎逐字节相同(+0.02%,主要是内嵌的 lpkg 换成了 8.0.0)。这个版本不跟进上游版本,所以体积没有变化。
  • 完整包 archive(lankeos_final_0.21_pkgs.tar.xz)2.44 GB(2.27 GiB),861 个包。
  • 构建工作区快照(lankeos_final_0.21_workdir_snapshot.tar.xz)13.7 MB。
  • 新增迁移工具(lankeos_final_0.21_lpkg_merge.tar.xz)417 KB。

稳定性与兼容性

本版本经过验证:

  • 全部 861 个包在 farm docker 容器中构建通过(farm/out 里 861 个 .lpkg)
  • 全量破坏性改动后重新打包并回填符号版本,861 个 .build_ok 全部重算,且逐一复核与当前配方内容一致(861/861 匹配,0 失配)
  • lpkg make test:1216 用例 / 144 套件 / 1215 通过 / 0 失败 / 1 跳过
  • lpkg make fuzz:11 个 harness 全绿(约 986 万次迭代,无崩溃样本)
  • farm cargo test:323 通过 / 0 失败,cargo fmt --check 与 cargo clippy --all-targets 0 告警
  • 仓库里 861 个真实 .lpkg 全量扫描,新归档判据零误报
  • LiveISO 内嵌的 lpkg 已更新到 8.0.0 (顺便所有包的安装也都修改了)

下一步计划 (Roadmap)

  • farm 本机并行调度器(阶段 1):把 run_build 的顺序队列换成 DAG 前沿调度器 + N 个本地 docker 槽位,验收标准是 -j1 与 -jN 的产物逐字节相同
  • farm 分布式构建(阶段 2/3):client/server + SSH,复用阶段 1 的 worker 池与前沿抽象
  • COSMIC 桌面(0.20 起在列,仍未开工)

开发者的话

0.21 的主题是 “把判据收成一份,把界面收成一层”。

(啊,天呐,是重构,我们没救了)

这个版本没有新包、没有上游版本跟进——861 个包里唯一版本号变了的包就是 lpkg 自己。所有工程量都在包管理器上。

我基本上...算了不说了你们自己看吧。

消费金额:¥452.17CNY API 请求次数:43,442 Tokens:10,773,665,193

AI真是太好用了你们知道吗 百亿Token这块。

我这几天简直算是疯魔了,在终于有时间自己审计一下那些之前完全用 AI 写的代码之后我发现这什么鬼啊,不该用启发式的地方给用上了,provides 语义混淆,非得来个 libsolv vercmp 语义桥接而不是直接用其默认语义比较...

完全就是根本不知道这写的什么代码。顺便 1000 多个测试都抓不完 bug,这 lpkg 质量真不行了,需要大修。那么大修改语义正好 breaking,我索性彻底用AI重写了一遍。

顺便弄了 fuzzing,然后它当天就抓到了三个 strip 缺陷。虽然都没有藏得很深,但是效果相当诡异——一个 sh_addralign = 2^56 的节区能让 memfd 被撑到 64 PiB,7 秒吃掉宿主 48 GB 内存。我只是跑个 fuzz 就会把桌面打崩。看得出威力了。

LankeOS可能几天内无commit但是不是断更了,是我这几天维护压力已经到了就算用AI写也得每天写12小时的状态。甚至我release note里变更部分都得用AI写了。我感觉自己不太行了,国庆假期最后两天给自己放个假。

—— Wtada233

10月3日 lpkg 7.2.0:全新终端界面 + 引入 fuzzing + 内核开 memcg

  • 新增 ui/term 输出层::: 阶段分隔条、==> ... [OK] 单行原地刷新、进度条(下载带字节/速率/ETA)、批次末 summary;非 TTY 降级纯文本
  • 安装拆成「下载 / 解压 / 安装 / 运行钩子」等阶段,逐包日志压缩成一行
  • 首次引入 make fuzz(libFuzzer + ASan/UBSan)与语料;当天抓到 strip.cpp 的三处畸形输入漏洞(ELF 头被覆盖 / 尺寸无上限 / 对齐值撑爆 memfd)
  • 内核开 CONFIG_MEMCG(linux release 3 → 4)——此前 docker --memory / systemd MemoryMax 都是空壳
  • 静态库 ar 符号索引按新位置重算并保留(不再丢弃);认不出的索引整份归档不剥 + 告警
  • 修两条中危:--root 下经符号链接祖先穿透、归档成员名守卫漏了 .lpkg_bak_ 命名空间
  • 紧接着的多轮并行审计收口:暂存目录改 mkdtemp、DIR_META 严格十进制、WAL 回滚 lstat 语义、恢复期清理前缀守卫、归档特殊文件拒绝、metadata 分帧字符等

10月4日 lpkg 8.0.0:provides 拆分 + rpm 版本语义

  • provides 拆成"虚拟 provider / SONAME"两个字段,池里只剩两个命名空间,零形状判断;索引版本块收紧为恰好 6 段,新增 provides_soname.db
  • 版本语义直接采用 rpm 的 EVR,删掉 +N 编码桥;farm 的 track/vercmp.rs 换成 libsolv rpm 算法的移植(3944 对 fixture 逐值对齐)
  • 861 个配方的 provides 键改名 provides_soname(纯文本迁移,不走 JSON 重序列化)

10月5日 符号版本 + farm 修复 + 全量回填

  • needed_so/provides_soname 支持 ELF 符号版本(保守匹配),farm 扫 verdef/verneed 自动产出;新增 base/so_spec 唯一实现,C++/Python/Rust 三方同一组真值表
  • 全套件回填符号版本 + 版本号迁移到 rpm 语义 + 重算全部 861 个 .build_ok
  • 修 abifix 的 SONAME 判据口径——此前会误报 734 个包并给它们白 bump release
  • 补一条"部分覆盖必须拒绝"的端到端用例(provider ⊂ need)

10月6日 畸形归档守卫 + 打包

  • lpkg 修 6 条审计缺陷,新增 archive/tar_guard(5 条判据,整包拒绝);"反斜杠一律拒"在真实仓库全量扫描上撞出 systemd 的误报,当天删除
  • filesystem 包修属主与 .gitkeep 残留,补 /var/lock、/var/run 符号链接
  • 重新打包 0.21 的 ISO / pkgs / workdir_snapshot,并发布 lpkg-merge 迁移工具

10月18日 进行 world rebuild + 大量修复

  • 全部 871 包 rebuild 并发布
  • 修改 Python 生态自举方式为 lanke-py-bootstrap 代替 Arch 的实现
  • 在修改自举方式后顺利在一天内迁移到 Python 3.15 并构建完成所有受影响包
  • KF 6.31.0 和 KDE Plasma 6.8.0
  • 修改众多坏 tracker,现在全部可运行
  • 所有包升级到最新版本
  • 更换默认 dbus-daemon 实现为 dbus-broker
  • 修复 lpkg 在进行 strip 时无法处理部分 ELF 的问题
  • 修复 farm 的 repack,换掉无法处理稀疏文件的 tar crate