跳转到主要内容

bea 0.2.0:一次安装,完整 Beancount 工具链

发布日期 阅读需 7 分钟Mike ThriftMike Thrift
bea 0.2.0:一次安装,完整 Beancount 工具链
本页总览

如果你曾经把一个可用的 Beancount 环境交给同事、新笔记本电脑或夜间 cron 任务,你就知道记账从来不是最难的部分。难的是工具链:匹配的 Python 版本、路径上的 bean-checkbean-query、为一张资产负债表拉进来的报表库,以及一个当你一问问题就重写文件的格式化工具。bea 0.2.0 于 2026 年 9 月 12 日发布,用一次安装取代了那份清单。bea 命令现在携带完整的原生 Beancount 工具链,在它自己配置的托管引擎中运行,并保持脚本和 AI 代理已经依赖的机器可读契约。

这是 0.2.0 的发布说明,按照我们内部跟踪发布的方式撰写:发布了什么、底层有什么变化、在进入软件包索引之前如何验证、它刻意不做什么,以及如何升级。如果你想看第一次运行的说明,0.1.0 发布文章CLI 快速入门是更短的阅读材料。

发布概览

两个渠道发布同一个命令。选一个,然后确认它能用版本号应答:

$ brew install bex-co/tap/bea        # macOS 和 Linuxbrew
$ uv tool install beancount-io       # 任何有 uv 和 Python 3.12 或更新版本的地方
$ bea --version
bea 0.2.0
bea 0.2.0
cli-v0.2.02026-09-12
引擎
beancount 3.2.3 beanquery 0.2.0
可选
beangulp 0.2.0 beanprice 2.1.0
python
3.12 3.14

0.2.0 发布卡片:标签和发布日期、托管引擎固定的 Beancount 和 Beanquery 版本、两个可选引擎功能,以及发布时安装并测试过的 Python 版本。

项目内容
版本0.2.0,标签 cli-v0.2.0,2026-09-12 发布到 PyPI 和 bex-co/homebrew-tap Homebrew tap
上一版本0.1.0,2026-09-09 打标签,早三天
变更集27 次提交涉及 CLI,119 个文件变更,约新增 12,300 行、删除 2,100 行
引擎固定基础引擎中 Beancount 3.2.3 和 Beanquery 0.2.0;Beangulp 0.2.0 和 Beanprice 2.1.0 作为可选功能
头条所有原生 Beancount 工具都在一个前缀下,由托管引擎提供;0.1.0 的 JSON 封装和退出码契约不变

底层变化:托管引擎

在 0.1.0 中,bea 像任何 Python 工具一样,把 Beancount 导入到自己的进程中。这可行,但让 CLI 的依赖图变成了 Beancount 的依赖图,并把"先安装 Beancount"留成了每个指南中未写明的步骤。

0.2.0 在程序中间画了一条线。bea 前端,即拥有命令、选项和渲染的部分,从不加载 Beancount、Beanquery 或内置的 Fava 报表代码。本地账本工作在托管引擎中运行:这是一个独立的 Python 环境,bea 从哈希固定的锁文件中配置并作为子解释器启动。前端跨该边界发送 JSON 请求并渲染返回的内容。你不需要安装 Beancount、把 bean-* 工具放到路径上,或考虑它们找到的是哪个 Python。

引擎的获取方式取决于渠道:

  • Homebrew 在安装期间创建前端和引擎环境。本地命令使用 keg 本地引擎,无需额外下载。
  • PyPIuv tool install 或 pipx)在首次使用时配置。第一个需要引擎的本地命令会下载固定的组合,这需要网络访问和路径上的 uv 一次。后续命令离线复用 ~/.local/share/bea/engine/<version>,如果你设置了 XDG_DATA_HOME,则在该目录下。

这个设计带来三个特性,每个都消除了一张我们已经见过的支持工单:

  1. 升级保持配对。 bea upgrade 把更新交给安装此副本的包管理器,然后重建匹配的引擎,因此前端和引擎永远不会漂移到不同版本。
  2. 损坏的引擎自我修复。 如果配置中途失败,托管环境会被丢弃,并在下次成功尝试时重建。路径上其他地方的杂散 bean-check 二进制文件会被忽略,不会被意外拾取。
  3. 重量级可选组件保持可选。 Beangulp 导入框架需要系统的 libmagic 库,Beanprice 引入报价获取依赖。两者都不在基础引擎中。你显式启用它们,并且只在引擎中启用。
$ bea engine status
$ bea engine enable beangulp     # 导入辅助;需要系统 libmagic 库
$ bea engine enable beanprice    # bean-price 报价获取

bea engine status 报告引擎是否已配置以及启用了哪些可选功能,不需要网络即可报告。如果首次配置失败,修复网络或 uv 后重新运行任何本地命令,例如 bea check。不要在旁边 pip install beancount:前端不会使用它。

每个原生工具,一个前缀

引擎是机制。面向用户的改变是对齐:上游 Beancount 项目发布的每个可执行文件现在都有一个 bea 对应命令,转发相同的参数并保留相同的输出。

$ bea check                                    # bean-check,加上 bea 的 --json 封装
$ bea format main.bean -o clean.bean           # bean-format:默认输出到 stdout,-i 重写
$ bea query "SELECT account, sum(position) GROUP BY account"
$ bea doctor context main.bean 2026-01-02      # 全部十一个 bean-doctor 操作
$ bea example --seed 1 -o example.beancount    # bean-example
$ bea treeify < balances.txt                   # treeify
$ bea ingest identify --config ingest.py inbox # Beangulp,在引擎启用后
$ bea price -e USD:yahoo/AAPL                  # bean-price,在引擎启用后
bean-check
bea check
bean-format
bea format
bean-query
bea query
bean-doctor
bea doctor
bean-example
bea example
treeify
bea treeify
beangulp
bea ingest bea engine enable beangulp
bean-price
bea price bea engine enable beanprice

对齐映射:虚线以上六个原生 Beancount 可执行文件开箱即用;虚线以下两个在你启用引擎中的该功能后转发到 Beangulp 和 Beanprice。

其中一些值得比表格中的一行更多说明。

bea check 就是 bean-check,叠加了 bea 的 JSON 封装:相同的验证、相同的错误消息,在 --json 下包含脚本已经解析的相同 validerrors 字段。

bea format 改变了行为,这是本版本中唯一可能让脚本惊讶的变化。在 0.1.0 中,bea format PATH 会重写文件。现在它把格式化后的文本打印到 stdout,并保持文件不变。--in-place (-i) 才是重写,--output FILE (-o) 写到其他地方,--check 是 CI 门禁,在需要格式化时以退出码 1 退出,--dry-run 列出会变化的内容。这遵循 bean-format,其默认值是安全的:读取路径并静默重写的命令无法先试运行。格式化是文本转换,不是解析,所以它不再拒绝有语法错误的文件;它对齐识别到的部分并保留其余部分。使用 bea check 检查有效性。

bea query 扩展了完整的原生表面。 它接受 BQL 作为参数、从 stdin 或交互式 shell 输入,交互式 shell 现在是上游 Beanquery shell,作为子进程启动,保留其 .format.output.run.set 命令。--format 选择 textcsvbeancount 渲染,--numberify 把金额拆成每币种一列,-o 写入文件,--source URI 直接传递原生 Beanquery 源。

bea doctor 暴露全部十一个 bean-doctor 操作lexparseroundtripdirectorieslist-optionsprint-optionscontextlinkedregionmissing-opendisplay-context。如果你曾经用 bean-doctor context 调试过记账问题,它就是同一工具,在同一个地址。

bea examplebea treeify 是原生生成器和原生树渲染器,原样转发。

bea ingestbea pricebea engine enable 后分别转发到 Beangulp 的 identifyextractarchive 以及 bean-price。无 Python 的 CSV 路径 bea import --csv 两者都不需要,保持不变。

一个规则把转发的命令联系在一起:doctorexampletreeifypriceingest 原样传递它们的参数给上游,并保留上游的输出和退出状态。这也意味着它们把账本作为自己的位置参数,如 bea doctor lex main.bean,而不是通过全局 --file。下面的封装和退出码类别描述 bea 自己的命令。

脚本可以继续信任的契约

机器可读的表面没有任何变动。全局 --json 仍然在 stdout 上放一个封装,包含 beatargetdatatruncated,以及在有限列表上附加 limit,在分页托管列表上附加 page。金额是十进制字符串,绝不是浮点数,日期是 ISO YYYY-MM-DD--json 隐含 --no-input;非终端 stdin 或真值 CI 变量也是如此,所以无人值守任务永远不会等待人类。--strict 即使在终端中也拒绝部分答案,每个读取命令的 --allow-errors 选择重新参与。

失败时 stdout 不写入任何内容,stderr 恰好写一个对象:

{
  "error": {
    "category": "validation",
    "message": "Ledger has 3 error(s). Pass --allow-errors to report anyway.",
    "exit_code": 1,
    "details": ["main.bean:1: Transaction does not balance: (2.50 USD)"]
  }
}
0
ok
1
validation
2
usage
3
auth
4
conflict

五个退出码和每个在 JSON 错误对象中携带的 category 字符串。脚本按数字分支;人类读取类别。

代码类别含义
0成功,包括预览和故意的重复跳过
1validation账本或验证错误,以及任何其他运行时失败的兜底
2usage参数错误、缺少目标或多余,或在 --no-input 下需要输入
3auth认证或权限失败,包括只读目标
4conflict并发更改、需要重复审核的导入,或结果未知的写入

两个细节对任何在失败时重试的人都很重要。非零退出并不普遍意味着什么都没有改变:add transactions --partial 可以写入接受的记录,format -i 处理多个文件时可以在一个失败前重写一些,cloud ledger create --clone 可以在克隆失败前创建账本。在重试变更操作前读取 error.result。托管命令把服务器的 HTTP 状态映射到同一张表,保留服务器自己的消息:401 和 403 退出码为 3,400 为 2,409 为 4,其他所有情况(包括速率限制)退出码为 1。CLI 无法知道结果的写入,如删除中途超时,退出码为 4 并说明,而不是猜测。

自动化指南端到端地演示了一个 jq 管道处理这个封装。

随行的修复

对齐发布也是关闭首个发布暴露的缺陷的机会。这些修复落在两个标签之间,每个都有回归测试:

  • 数字以定点文本写入,绝不使用科学计数法,包括 bea init 渲染的期初余额。写着 1E+3 的账本技术上是有效的,但实际不可读。
  • 成本批次在 JSON 序列化中保留日期和标签,批次标签在写入交易时正确转义。
  • 导入期间显式零分录是真实金额,而不是被读为"省略,请帮我平衡"。
  • CSV 导入通过一个严格的读取器。 表头发现过去会剥离列名,而提取保留原始键,因此文档承诺接受的填充表头会作为缺失列失败。现在名称只剥离一次,映射的列必须恰好出现一次,未闭合的引号在写入任何内容之前以其行号失败。
  • BQL 加载准确的账本路径,而不是 URL 解析的连接字符串,因此不寻常的路径像 CLI 其余部分一样解析。
  • bea balance <term> 只合计它显示的内容。 保留的父级不再报告被排除子级的合计,无关的未定价持仓不再使 USD 选择失败,封装报告应用的过滤器。报表上格式错误的 --account 模式以退出码 2 退出,作为它应得的使用错误。
  • JSON 模式 stderr 始终是一个对象,即使在失败前有被容忍的警告。
  • 托管凭据及早且一致地失败:包含空白的 BEA_TOKEN 在任何请求前被拒绝,撤销的凭据由 cloud status 和账本命令以相同方式报告,owner/name 在确认提示或认证调用前被验证。cloud logout 保留 BEA_TOKENcloud ledger list --json 回显它实际服务的页面。
  • Homebrew 配方固定了确切的 PyPI 工件 URL,因此 tap 安装和 PyPI 安装可证明是相同的字节。

在你看到之前如何验证

发布是一个声明,管道是证据。cli-v0.2.0 标签必须指向 main 上的一个 pyproject.toml 版本完全匹配的提交;工作流拒绝任何其他情况,包括预发布后缀。从那里开始:

  1. 完整检查套件首先运行。 make check-all 覆盖 lint、格式化、严格 mypy、死代码检测、生成参考漂移检查和测试套件。发布拉取请求记录了 635 个测试通过。
  2. 引擎锁导出并哈希固定,源码分发和 wheel 构建一次。每一步后续测试那些确切的工件,而不是重建。
  3. 三个操作系统和两个 Python 上的干净安装。 wheel 通过 uv tool 安装,sdist 通过 pip 在 Linux、macOS 和 Windows 上安装,Python 3.12 和 3.14,包括可选的 AI 额外功能。一个 Homebrew 任务在 macOS 和 Linux 上通过临时 tap 安装 sdist。
  4. 发布是顺序的且无令牌的。 PyPI 通过可信发布接收工件,因此没有长期存在的 API 令牌会泄露;GitHub Release 创建时带有发布证明;Formula/bea.rb 被推送到公共 tap,包含 PyPI 实际服务的 sdist URL 和哈希。
  5. 发布后冒烟测试从真实索引安装。 单独的任务从 PyPI 和公共 tap 安装固定版本,并针对已安装的可执行文件运行相同的客户冒烟测试。失败不回滚任何内容,但确实意味着在告诉任何人之前发布需要关注。

这篇文章写在第五步的另一侧。

从 0.1.0 升级

通过安装你副本的包管理器运行升级,或让 bea 来做:

$ bea upgrade --check      # 报告已安装和最新版本以及将运行的命令
$ bea upgrade              # brew upgrade bea,uv tool upgrade beancount-io,或 pipx upgrade beancount-io

包管理器完成后,bea upgrade 刷新托管引擎,使两者保持配对。然后检查三件事:

  • 任何运行 bea format PATH 重写文件的脚本现在需要 bea format -i PATH。旧默认值无法预览,新默认值可以。
  • 任何依赖 format 捕获语法错误的脚本应该为那调用 bea check,因为格式化不再解析。
  • PyPI 安装在上次升级后的第一个本地命令需要一次网络和 uv,以便配置引擎。Homebrew 安装不需要任何东西。

脚本已经解析的所有内容、封装键、十进制字符串和退出码都不变。封装中的 bea 字段现在读作 0.2.0

此版本不做的事

  • 托管目标未实现。 没有 --ledger 标志;本地命令读取本地文件,绝不隐式上传。托管账本在 bea cloud 下管理,作为 git 克隆进行工作。
  • bea ask 仍然需要 ask 额外功能和 Beancount.io 凭据,并且不支持 --json。默认安装不携带 AI 依赖。
  • Beangulp 和 Beanprice 是可选加入,Beangulp 需要系统的 libmagic 库。bea import --csv 在没有这两者的情况下覆盖银行导出。
  • 转发的原生命令不发出封装。 如果你需要 doctor 操作的结构化输出,这是我们想听到的请求。

自标签以来,main 已经拾取了 0.2.0 的第一轮 QA,它将搭载在下一次发布中:bea format 读取 stdin 作为过滤器,其 -o FILE 模式以命名写入内容的封装应答;--json check 拒绝仅 bean-check 的标志,--jsondoctorexampletreeify 上被直接拒绝,因此脚本不能把原生文本误认为封装;--json query -o FILE 原子地写入封装到文件,--numberify 也应用于 JSON;bea engine status 命名哪个引擎层级在服务;以注释开头的 BQL 查询运行;原生透传 --help 在引擎配置之前工作;查询 shell 的 .output 在重定向失败后恢复原始流。

下一步去哪里

让账本保持代码化

一个可以一行安装的工具链是可以交给任何人的工具链:联合创始人、簿记员、CI 运行器、AI 代理。Beancount.io 提供保持透明、版本控制和可复现的纯文本记账,bea 是保持本地账本诚实的命令,托管服务是你的团队、手机和助手相遇同一账本的地方。安装 bea 并运行你的第一次检查,如果发布做了什么出乎意料的事,GitHub 仓库是我们想听到的地方。

分享这篇文章

来源:https://beancount.io/zh/blog/2026/09/16/bea-0-2-0-one-install-whole-beancount-toolchain

发布日期: 2026年9月16日