
>
从代码完工到 apt install ./tux-im.deb:mypy strict、CI 演化、deb 打包。
本篇是 《TUX IM 开发日志:从零到 0.1》 系列第 8/8 篇。
第 8 篇 · 发布 0.1.0:mypy strict、CI、deb 打包
代码完工不等于能发布。从最后一行代码到 apt install ./tux-im_0.1.0-12_all.deb 之间,有 25 个 commit 在做"工程化"——lint、类型检查、CI、deb 打包、版本号管理。这篇讲这部分。
mypy strict:0 errors 是怎么逼出来的
commit 153587c mypy strict: 0 errors 是一个里程碑——整个项目 27 个 Python 文件,mypy --strict 通过。
mypy strict 包含什么:
-
所有函数参数和返回值必须标类型。
-
不能有
Any类型逃逸。 -
不能隐式
Optional。 -
不能
# type: ignore滥用。
为什么值得做?
-
重构时不慌——改一个函数签名,mypy 立刻把所有调用方列出来。
-
新人 onboarding 快——IDE 自动补全准确,不用读整个文件猜类型。
-
运行时类型 bug 几乎消失——
AttributeError: 'NoneType' object has no attribute 'x'这种经典 Python 错误在 strict 下根本写不出来。
>
几个有代表性的修复:
-
config.dict → config.dictionary——避免跟 Python 内置
dict类型同名引起混淆。 -
shortcut handler 类型注解——之前都是
Callable没有任何参数信息,mypy 看不到。 -
PEP 563 注释解析——
from __future__ import annotations之后 dataclass 字段的list[int]这种泛型在 Python 3.9 之前需要typing.List[int]。 -
ASR 模块整体加 **
ignore_errors**——ctypes 的 cfunc 签名跟 mypy 不兼容,没法 strict。这是务实的妥协。
lint 债务是怎么还清的
项目历史上有过 53 个 ruff error。处理方式不是一次 commit 修完,而是三步走:
-
74ee0af style: manual lint fixes for the non-fixable 53 ruff errors——手修不能自动修的。 -
f36218d style: auto-fix ruff lint debt——ruff check --fix自动修。 -
2d5daf6 style: rename _MAX_PINYIN_LEN to _max_pinyin_len per ruff N806——按 N806 规则重命名常量。
之后 CI 强制 ruff check . 在 push 时跑,新债务不再积累。
CI:GitHub Actions 的演化
从 d88808a ci: add GH Actions workflow 开始,CI 平台是一步步搭起来的。每一步都有坑:
坑 1:respx 是 PyPI 包不是 apt

第一次写 CI 时,我在 apt-get install 里加了 python3-respx——但 respx 是测试 HTTP mock 库,只在 PyPI 有,apt 仓库里没有。CI 跑 build 步骤时 pip 装不上 → 整个 workflow 失败。
修复 f942aff ci: respx is on PyPI, not apt; move to pip install:换成 pip install respx。
坑 2:debian/control 缺依赖
CI 在 dpkg-buildpackage 时报 missing build dependencies。日志显示:
dpkg-checkbuilddeps: error: Unmet build dependencies:
build-essential python3-httpx python3-sounddevicedpkg-checkbuilddeps: error: Unmet build dependencies: build-essential python3-httpx python3-sounddevice
这些依赖在 debian/control 里漏了。修复 d82079d ci: add missing build deps。
坑 3:sounddevice 在 Ubuntu noble 仓库里没有
更狠的来了:python3-sounddevice 在 Ubuntu 22.04 (jammy) 有,但 noble (24.04) 仓库里还没有。CI 跑在 noble runner 上,仍然失败。
修复 724c4a2 fix(control): drop python3-sounddevice from Build-Depends:从 build deps 里删掉,让它运行时再装(不强制打包时存在)。
坑 4:upload-artifact@v4 拒绝 .. 路径
CI 把 .deb 上传为 artifact 时报:
错误
Error: Failed to CreateArtifact: path '../tux-im_*.deb' is outside of the workspace原因:dpkg-buildpackage 默认把产物输出到上级目录(约定俗成),但 GitHub Actions v4 artifact 不允许 .. 相对路径。
修复 39ff09b ci: move built .deb into ./dist/:改 dpkg-buildpackage 输出到 ./dist/。
CI 流水线最后的样子
从 6 个 fix commit 后,CI 包含 4 个 job:
-
lint——
ruff check .+mypy --strict .。 -
test——
pytest tests/(当前 38 个测试)。 -
build-deb——
dpkg-buildpackage -us -uc -b,上传 .deb 为 artifact。 -
release——只在 **
v*** tag push 时触发,把 .deb 附到 GitHub Release。
关键设计:test + build-deb 在每次 push / PR 时跑(gate),release 只在 tag push 时跑。这样 "Releases" 页是干净的发布历史。
deb 打包:apt install ./tux-im.deb
用户拿到 .deb 后一条命令装好:
用户安装流程
sudo apt install ./tux-im_0.1.0-12_all.deb
ibus restart装什么?
-
/usr/share/ibus/component/tux-im.xml——IBus 组件描述文件,让 ibus-daemon 认识 TUX IM。 -
/usr/lib/python3/dist-packages/tux_im/——Python 包。 -
/usr/bin/ibus-engine-tux-im——引擎启动脚本(symlink 到python3 -m tux_im.main)。 -
/usr/bin/tux-im-setup——设置面板(tux_im.ui.settings:main)。
postinst 脚本自动跑 ibus restart。
"清干净再提交"原则的代价
你可能在多个 commit message 里看到"清干净"这个词——这不是矫情,是原则:
>
原则:CI / lint / 类型检查错误必须全部修干净再合并。
不接受"先 shrink CI 跑通再说"或"先发布后修"的 deferred 方案。
代价:有些 commit 会花 1 小时才 push,只为了 53 个 lint error 全部清干净。但收益是:
-
主线永远干净。
-
每个 PR 只需要关心自己的 diff,不用同时修历史债务。
-
新人 clone 仓库就能
pytest全过。
v0.1.0-12:发布日期
截至最新,仓库标签:
git tag -l
v0.1.0-10
v0.1.0-12版本号约定:v0.1.0-N,N 是 deb revision。v0.1.0 是源码版本,-12 表示这是第 12 次 deb 重新构建(每次 Build-Depends 改了都要 rebuild)。
从 aa4b094 到 v0.1.0-12:
-
46 个 commit
-
约 4000 行 Python
-
38 个测试
-
mypy strict 0 errors
-
ruff 0 errors
-
3 种 input mode(pinyin / wubi / wbpy / latin / emoji / google_pinyin 共 6 种)
-
1 个 ASR 子系统
-
1 套 CI + 1 套 deb 打包
本篇小结(系列完结)
-
mypy strict 是值得的——重构不慌、IDE 补全准、运行时类型 bug 消失。
-
CI 演化是踩坑驱动的——4 个 fix commit 才稳定。
-
deb 打包 = 一次性学习 dpkg + debhelper,但有模板之后就是个配置文件。
-
"清干净再提交"是维护性的核心原则——主线永远干净,新人友好。
>
系列完结。从 aa4b094 到 v0.1.0-12,46 个 commit 讲述了一个输入法引擎从空仓库到 deb 包的完整故事。
下一阶段:v0.2.0 路线图(feishu 实现 word-by-word 选择、整句输入完善、Linux Wayland 支持、更多 ASR provider)。
评论