sow add
sow add 是主要的写入路径。它解析你指定的包,从包头推导格式与架构,执行 Dist 的成员策略,并且——
除非你加 --skip——在返回前重建全部受影响的索引。命令退出码为 0 时,客户端已经能看到新包了。
语法
参数
| 参数 | 说明 | 默认 |
|---|---|---|
-R, --recursive |
递归进入 PATH 目录的子目录 |
关闭(只扫顶层) |
--skip |
只更新期望状态,不构建 | 关闭 |
-j, --jobs N |
解析、哈希与渲染的并发 worker 数 | 逻辑 CPU 数 |
-C, --workdir DIR |
工作区发现的起始目录 | 当前目录 |
-r, --repo NAME |
选择一个仓库 | 按选择规则 |
-d, --dist NAME |
选择一个 Dist;可重复 | 按选择规则 |
-T, --timeout DUR |
等待锁的最长时间;0 无限等待 |
0 |
-N, --no-wait |
锁被占用时立即失败 | false |
--json |
输出版本化 JSON envelope | false |
输入与目标
PATH 可以是文件或目录。目录默认只扫描顶层,除非你加 -R。
最终必须确定恰好一个仓库与至少一个目标 Dist——见选择规则。RPM 与 DEB 混合批次是允许的:每个包只会被考虑放进格式相同的目标 Dist;一个包如果没有任何兼容目标,则该包失败。
SOW 绝不从 manifest、目录名或宿主机 OS 推断目标。
汇总行给出 Operation ID、逐项计数、成员增减、新的 Desired Revision、Built Generation,以及仓库是否
留在 dirty 状态。随后是每个输入一行 item,顺序稳定。
逐项状态
每行 item 带一个总体 status,以及 dists= 中的逐 Dist 判定。
| 状态 | 含义 |
|---|---|
accepted |
新建 Package Object,且至少增加一条 Membership |
reused |
内容已存在于本仓库;可能只是新增了 Membership 引用 |
excluded |
策略把它从所有目标 Dist 中移除——看 dists= 区分是 excluded 还是 limited |
failed |
该包被拒绝,error= 字段说明原因 |
reused 表示内容幂等:同一个文件加两次绝不会产生第二个对象或重复 Membership。默认重复执行
add 时还会收敛所选 Dist;Dist 已经最新时 Generation 不变,先前 --skip 或配置变更留下
dirty 状态时,则会补做构建并可能推进 Generation:
把同一个对象加进第二个 Dist 同样是 reused——包池只保留一份,只是多了一条 Membership。
如果仍想保留 dirty 批次,应再次显式使用 --skip。
架构是读出来的,不是猜的
add 从包头读取格式与原生架构,再对照工作区许可表。不在许可表中的架构会让该包失败,并明确告诉你
要改什么:
它不会创建目录,也不会修改 sow.yml。
RPM 的 noarch 与 DEB 的 all 是架构中性(neutral)的。它们只产生一个 Package Object 与一条
Membership,但会渲染进目标 Dist 的每个有效架构视图。它们不会自动扩散到你没有用 -d 选中的 Dist。
策略:exclude 与 limit
合并进目标 Membership 之后,SOW 会在完整的 Dist 候选集上重新求值 exclude,再求值 limit。被策略
移除的包会被明确报告,不算解析失败。
这里 trixielim 配了 exclude: [{kind: [dbgsym]}] 与 limit: 1。dbgsym 包被规则排除;
libpq5 18.2-1 在版本上限下输给了 18.3-1,报告为 limited。两者的顶层状态都是 excluded,
靠 dists= 字段区分。
limit 按 (二进制包名, 原生架构) 分组,因此 18.3-1:amd64 与 18.3-1:arm64 在 limit: 1 下都能
留下。同一次运行中,一个包可以被某个 Dist 接受、被另一个 Dist 跳过。
exclude 与 limit 移除的是真实的期望成员。之后放宽策略不会把它们变回来——pool/ 里残留的字节
不构成候选集。请重新执行 sow add。
部分成功的批次
即使同批有失败项,合法且无冲突的包依然会提交。失败的输入原地不动,各自带自己的错误信息,命令退出
3:
如果一个都没被接受,整个操作以退出码 6 被拒绝,仓库保持原样:
没有 rejected/隔离目录。
–skip
--skip 在期望状态提交后就停下。公开的 pool/ 与 dists/ 字节不变,Built Generation 保持原位,
仓库变为 dirty。新包字节被持久保存在私有 pending 存储中,直到下一次 build 才发布。
pending=4/2326 表示私有存储里有 4 个对象、共 2326 字节在等待。它们不会出现在
sow changes 中——只有成功的 build 才会把它们提升进可交付树。
批量导入时用 --skip,最后一次性收敛:
处理顺序
一次 add 的执行顺序如下:
- 取得仓库写锁,并恢复任何未完成的 Operation。
- 在 SQLite 中提交一条
plannedOperation。 - 只读解析输入,计算逻辑坐标与输入字节 SHA-256(RPM 还会计算 signature-neutral payload digest)。
- 校验架构许可表,并查询已有坐标。
- 只对确实全新的坐标,在 stage 副本上执行可选的 RPM 签名并计算最终 SHA-256,再校验内容与路径唯一 性。
- 合并目标 Membership,然后在完整 Dist 集合上执行
exclude与limit。 - 提交期望状态;新字节写入私有 pending 内容存储。
- 除非给了
--skip,把仍被需要的 pending 对象发布进pool/并渲染索引——一次命令中每个 Dist 最多 构建一次。
任何模式下,输入文件都不会被修改、移动或删除。
RPM 签名模式
Managed 模式的 RPM 包签名在 sow.yml 的 signing.rpm.packages.mode 中配置,命令行没有覆盖开关。
| 模式 | 行为 |
|---|---|
never |
完整保留输入字节 |
fill |
无签名或签名不受信任时用配置 key 签名;已有能被 trusted_keys 验证的签名则保持字节。配置了 key 时的默认值 |
always |
确保最终包由配置 key 有效签名;否则对 stage 副本重签 |
没有配置 key 时只能用 never。
由于签名包含非确定字段,SOW 无法先重签再比较最终哈希。重试幂等因此建立在坐标上:输入字节完全相同
则直接复用;RPM signature-neutral digest 相同、且既有对象满足当前策略时也复用。payload digest 不同,
或既有对象已不满足策略,则是硬冲突——add 不会静默地对同一坐标原地重签。
退出码
| 码 | 触发条件 |
|---|---|
0 |
全部输入被接受或复用;索引已重建(或因 --skip 跳过) |
1 |
运行时 I/O、解析器、渲染器或签名失败 |
2 |
用法错误、工作区未找到,或仓库/Dist 选择有歧义 |
3 |
部分批次——至少一项已提交,至少一项失败 |
4 |
仓库锁被占用,且给了 --no-wait 或 --timeout 到期 |
5 |
完整性或恢复错误,包括在 applied 之后构建失败 |
6 |
一个都没接受——架构不受支持、没有兼容的目标 Dist,或坐标冲突 |
参见
- sow rm —— 反向操作
- sow build ——
--skip之后的收敛 - 成员策略 ——
exclude规则与limit语义详解 - 签名模型 —— 两条信任链与 key 引用
- 包引用 —— item 行上打印的坐标文法