这是本节的多页打印视图。 .
功能
- 1: Plain 平面仓库
- 2: Managed 工作区
- 3: 包池与元数据视图
- 4: 成员策略
- 5: 签名模型
- 6: 事务与恢复
- 7: 可观测与审计
SOW 用两种模式创建仓库索引。可以随时重建的包目录使用 Plain; 需要持续更新、策略、签名和发布的仓库使用 Managed。
功能对照
| 功能 | Plain | Managed |
|---|---|---|
| RPM 与 DEB 索引 | 一个目录,可包含任一种或两种格式 | 一个仓库,分别使用不同 Dist |
| 持久化成员与构建快照 | — | 支持 |
| 分架构视图 | — | 支持,包含架构无关包 |
| 包排除 | --pigsty 使用固定清理规则 |
按 Dist 配置 |
| 版本数量限制 | sow prune 清理旧版 RPM |
按 Dist 配置 |
| 元数据签名 | — | RPM 和 DEB |
| RPM 包签名 | --sign-with |
never、fill、always |
| 中断恢复 | 重新运行 create 或 prune |
操作记录和发布尝试 |
| 操作日志与 JSONL 导出 | — | 支持 |
| 发布目标 | — | Filesystem 与 R2 |
SOW 自行解析包并生成索引,无需 createrepo_c、dpkg-scanpackages 或 reprepro。
RPM 包签名使用主机上的 rpm 和 GPG 环境;元数据密钥引用解析为 GPG 密钥标识时,也需要 gpg。
生成格式
| 格式 | Plain | Managed 增加的内容 |
|---|---|---|
| RPM | primary、filelists、other XML 和 repomd.xml |
架构视图,可选 repomd.xml.asc |
| DEB | Packages、Packages.gz |
Release、by-hash 索引,可选 InRelease、Release.gpg |
RPM XML 使用 gzip 压缩。SOW 不生成 SQLite rpm-md、zchunk、modulemd 或源码包索引。 生成结果由你配置的 HTTP 服务或对象存储对外提供。 客户端细节见平台与集成。
按任务阅读
1 - Plain 平面仓库
用 sow create 为一个 RPM 或 DEB 软件包目录生成索引。索引与包文件放在一起。
该命令不读取 sow.yml,也不创建状态数据库。
输入与输出
SOW 只扫描目录顶层以 .rpm 或 .deb 结尾的普通文件,不递归,也不跟随包文件符号链接。
包名、版本和架构来自包内信息。SOW 会拒绝源码 RPM(src 和 nosrc)。
| 输入 | 生成文件 |
|---|---|
| RPM | repodata/repomd.xml 和 gzip 压缩的 primary、filelists、other XML |
| DEB | Packages 和 Packages.gz |
| 两种格式 | 两组索引 |
RPM 包路径使用文件名,DEB 的 Filename 使用 ./<文件名>。SOW 会把所有有效版本收入索引。
同一包坐标对应不同字节时,构建报冲突。SOW 会拒绝没有受支持包的目录;--pigsty
完成中断的清理操作时例外。
默认保留包文件和无关文件。某种格式不再有包时,SOW 删除该格式的旧索引。
对于 RPM,SOW 保留当前与紧邻上一轮以校验和命名的元数据,删除更早的 SOW 格式元数据,并替换不完整输出。若 repodata/ 中存在 SQLite 数据库、modulemd、comps 或 .asc 签名等非 SOW 文件,sow create 会清理这些外来元数据,并在 dropped_metadata(--json)和 stderr 告警中报告。
生成目录的权限为 0755,生成文件为 0644。
构建与重复运行
普通未签名构建只读取每个选中包一次,同时计算 SHA-256 并解析元数据。
--jobs 控制并行数,默认为逻辑 CPU 数。SOW 根据解析结果生成并检查索引。
替换索引前,SOW 再次核对包文件列表、文件身份、权限、大小和修改时间。
构建期间应停止其他程序修改包文件。文件状态发生变化时,命令停止发布并返回退出码 5。
相同包字节和选项生成相同元数据。如果生成文件与现有文件相同,命令报告
文本字段 noop=true 或 JSON 字段 noop: true,保留现有文件。
RPM 元数据的 revision 和 data timestamp 默认都是 0。更新供 EL7 YUM 使用的已有仓库时,
请设置发布时间:
该选项只改变 repomd.xml,不改变压缩 XML、包文件或 DEB 索引。时间戳应从已有仓库的值
继续递增;操作方法见 YUM 迁移。
小文件 .sow-plain-rpm-history.json 跟踪 RPM 元数据保留范围。
没有变化的构建仍保留同一份上一轮索引。复制目录后还要继续构建时,一并保留该文件。
软件包保留仍由维护者负责,详见 RPM 索引保留。
清理旧版 RPM
使用 sow prune 在 Plain 目录中按每个 RPM (name, arch) 仅保留最高 EVR 版本:
sow prune 按原生 rpmvercmp 规则(包含 ~ 预发布与 ^ 后置快照算符)比较版本。它先更新 repodata/,再删除被取代的 .rpm 文件,确保客户端不会看到指向缺失软件包的索引。如果目录中已有 repo_complete,sow prune 会自动刷新该标记。与 sow create 不同,当 repodata/ 中含有非 SOW 外来元数据文件时,sow prune 会拒绝执行。
构建中断
SOW 先在暂存区生成并检查元数据,再替换公开文件。这些替换不构成跨全部 RPM 和 DEB 文件 的单个事务,中断可能留下混合版本或不完整索引。
再次运行相同的 sow create 命令即可。它会清理自己留下的临时文件,并根据目录中当前的包
重新生成索引。Plain 模式没有操作日志恢复,JSON 字段 recovered 始终为 false。
对在线服务,在单独目录构建,成功后再把完整目录切换上线。需要记录成员关系、构建快照和 事务恢复时,使用 Managed 模式。
Pigsty 清理与完成标记
--pigsty 根据包内信息删除 DEB i386 包和 Patroni 3.0.4 包。
它不会仅因 RPM 架构为 i386、i486、i586 或 i686 而删除该包。
SOW 保留 annobin-docs 等文档包,除非命中上述清理规则。
索引更新和包清理完成后,SOW 写入 repo_complete。每行包含 SHA-256、两个空格和保留的
包文件名,按文件名排序。
SOW 在替换公开文件前移除旧标记,最后写入新标记。采用此约定的使用方应等待标记出现。
如果构建中断且标记缺失,再次运行带 --pigsty 的命令。目录已有 repo_complete 时,
不带 --pigsty 的命令会拒绝重建。
RPM 包签名
此选项为没有可解析内嵌签名的 RPM 签名。加上 --overwrite 可重签所有保留的 RPM。
签名需要主机上的 rpm 命令和可用 GPG 密钥。SOW 对暂存副本签名并检查,再先替换包文件、
后替换索引。这条路径会多次读取包字节。
密钥带口令时,可传入 --passfile PATH 或设置 SOW_GPG_PASSFILE;启用签名仍需
--sign-with。完整文件格式见口令文件。
Plain 模式不读取 Managed 的 sow.yml 配置。
Plain 模式不签仓库元数据。Managed 元数据签名和包签名策略见签名。
并发命令
sow create 锁住目标目录及其父目录。用 -N 在其他 SOW 写者占锁时立即退出,
或用 -T 30s 最多等待 30 秒。默认等待到锁可用。
完整选项与结果字段见 sow create。
2 - Managed 工作区
定期更新的仓库适合使用 Managed 模式。SOW 记录包成员关系、执行 Dist 策略、构建元数据, 并保留操作历史。
工作区、仓库与 Dist
| 术语 | 用途 |
|---|---|
| 工作区 | 保存 sow.yml、私有 .sow/ 状态和允许的架构族 |
| 仓库 | 拥有独立包池、Dist、SQLite 数据库、构建快照(Generation)和发布状态 |
| Dist | 按名称组织一组包,格式为 rpm 或 deb |
| 架构视图 | 为某种架构的客户端提供 Dist 中的包索引 |
一个仓库可以同时包含 RPM 和 DEB Dist。el9、trixie、el9-beta 等 Dist 名称由你指定,
不会自动检查操作系统兼容性,也不会建立版本晋级流程。
同一包可以属于一个仓库中的多个 Dist,无需复制包文件。架构无关包在每个 Dist 中有一份 成员关系,并出现在该 Dist 的各个适用架构视图中。不同仓库分别保存自己的包对象。
公开文件与私有状态
- <workspace>/
- sow.yml
- .sow/
- workspace.lock
- workspace-ops/
- repo-locks/<repo>.lock
- <repo>.db
- <repo>/
- stage/
- recovery/
- pending/
- retained/
- transitions/
- <repo>/
- pool/
- dists/
对外服务或发布完整的 <workspace>/<repo>/ 目录。sow.yml 和 .sow/ 保持私有。
需要保留可继续工作的工作区时,一起备份配置、私有状态和包文件。
详见仓库布局。
创建与配置
初始配置使用 schema: sow/v3,架构族为 [x86_64, aarch64]。
除非配置中已经声明仓库,init 不会创建仓库。仓库与 Dist 的创建、删除命令会更新 sow.yml。
编辑 sow.yml 设置架构限制、成员策略、签名和发布目标,然后检查配置:
解析器拒绝未知字段。config check 还会比较配置与已初始化状态,并解析签名引用。
config show --all 展开默认值。配置接受 amd64、arm64,分别规范化为
x86_64、aarch64;DEB 索引仍使用原生名称 amd64、arm64。
要防止删除整个仓库,设置 protected: true。它会阻止 repo rm(包括带 -f 的调用),
但不限制包和 Dist 的修改。
期望状态与构建状态
期望状态记录各 Dist 应包含哪些包;构建状态记录最近完成的元数据构建快照。
默认情况下,add 和 rm 更新成员后会构建相关输出。带 --skip 时,只更新成员关系,
不构建。新包字节先保存在私有 pending/ 中,构建时再进入包池。
dirty 表示仓库需要构建,公开目录仍对应上一次完成的构建快照。
策略、架构或签名变化也会使 Dist 变脏。成员关系或构建状态仍引用某个架构时,SOW 会拒绝从配置中删除它。如果已发布到目标的某个 Dist 或架构随后被移除,必须先执行覆盖整库的 sow build -r REPO 生成新的完整构建快照,之后 sow publish 才会撤下远端已移除的入口指针。
新 Dist 会立即获得各架构的有效空索引。已配置元数据签名时,空 Dist 也会签名。 移除最后一个成员后,索引为空,但入口仍保留。
查找工作区
Managed 命令按以下顺序查找最近祖先目录中的 sow.yml:
- 指定
-C/--workdir DIR时从DIR开始,否则从当前目录开始。 - 没有找到工作区时,从
$SOW_DIR开始查找。 - 两次都没有找到时,返回工作区发现错误。
命令使用找到的第一个配置,即使该配置有错误。--workdir 只改变工作区查找起点,
不改变进程目录。相对输入路径仍相对于当前目录:
查找起点可以是符号链接,但搜索沿输入路径的祖先目录进行。工作区内的符号链接会阻止
隐式推断仓库或 Dist。需要时使用物理工作区路径,并明确指定 -r、-d。
Plain 命令 sow create 不参与工作区查找。
选择仓库与 Dist
仓库选择使用第一个可用结果:
- 显式
-r/--repo NAME。 - 工作区查找起点所在的仓库。
- 工作区中唯一的仓库。
add、rm 和 ls 按以下顺序选择 Dist:
- 显式
-d参数;可重复使用,选择多个 Dist。 - 工作区查找起点所在的 Dist。
- 所选仓库中唯一的 Dist。
如果仍有多个选择,命令要求显式指定。
build、check 和 status 默认覆盖所选仓库的全部 Dist。
show 搜索所选仓库,where 搜索工作区全部仓库。支持 -d 的命令可用它缩小范围。
各命令的选择规则见命令参考。
重复初始化与升级
再次运行 init 会补齐配置中尚未初始化的仓库和 Dist。已有状态只做检查,不重置构建快照。
给已有 Dist 新增架构后,运行 build 生成新视图。
初始化已提交部分对象、后续又失败时,命令返回退出码 3 并报告已完成内容。
修正错误后再次运行 init。
SOW 0.7.1 与 0.5.0 至 0.7.0 均使用数据库 schema v13。从这些版本升级均无需迁移数据库。
对于 0.3 或 0.4 工作区,先备份完整工作区,再执行
sow repo migrate。
不要手动修改 SQLite schema 版本。
数据库还缓存解析后的包信息。后续构建可以复用缓存和文件指纹;sow check 则读取包字节
并计算哈希,执行完整一致性检查。
3 - 包池与元数据视图
Managed 仓库将每个包对象保存在 pool/ 中。Dist 和架构视图只包含引用这些包字节的索引。
存储范围是一个仓库。同一个包放入其他仓库或发布目标后,分别占用相应仓库或目标的命名空间。
RPM 视图
RPM Dist 为每个配置架构生成一个元数据目录:
- demo/
- pool/
- e/example/example-1.0-1.x86_64.rpm
- d/demo-data/demo-data-1.0-1.noarch.rpm
- dists/el9/
- x86_64/repodata/
- <sha256>-primary.xml.gz
- <sha256>-filelists.xml.gz
- <sha256>-other.xml.gz
- repomd.xml
- aarch64/repodata/
- ...
- x86_64/repodata/
- pool/
x86_64 视图包含 x86_64 和 noarch 包,aarch64 视图包含 aarch64 和 noarch 包。
两个视图引用同一份 noarch 文件。
包位置相对于架构目录:
客户端指向 dists/el9/x86_64/,服务端提供完整 demo/ 目录,使相对包路径可以解析。
sow check 检查索引路径没有逃出仓库,并指向预期包对象。
APT 视图
APT 索引使用相对于仓库根的路径:
每个配置架构在 dists/<dist>/main/binary-<arch>/ 下有 Packages、Packages.gz
和 SHA-256 by-hash 条目。Dist 根包含 Release,已签名时还有 InRelease、Release.gpg。
all 包出现在每个适用架构索引中,包体仍只有一份包池对象。
SOW 不生成每视图包副本,也不生成每架构 Release 文件。
为 dnf reposync 导出
默认 dnf reposync 会拒绝越过其单仓库下载目录的 Managed RPM 路径。
此工作流可使用独立仓库导出:
RPM Dist 必须属于当前构建快照且处于干净状态。即使后续构建只更新了其他 Dist,只要该 RPM Dist 状态干净,也可以直接导出,因为 SOW 会把未变更的视图沿用到当前构建快照中。运行前先检查导出前提。
导出包含本地包路径、独立 repodata、清单和 .sow-export.json 完成标记。
目标目录应位于仓库和所有已配置 filesystem 发布目标的预期路径(endpoint + prefix)之外。
默认复制文件。--hardlink 要求同一文件系统,使导出包与源包共用 inode,因此应保持导出文件只读。导出不会创建 Dist 成员或保留构建快照,也不是 sow publish 的输入。
交付完整仓库
在线更新使用已配置的发布目标。删除某个 Dist 或架构后,先运行覆盖整库的 sow build -r REPO 再发布,使当前构建快照覆盖完整仓库并允许 sow publish 安全撤下远端已移除的入口指针。需要其他传输方式时,先将完整、已稳定的
pool/ + dists/ 复制到单独暂存目录,检查副本后再切换上线。缓存中的索引所引用的包 URL
应保持可访问,直到缓存过期。完整传输流程见对外服务。
只复制某个 RPM 架构目录,会使包路径无法解析。
sow changes 0 在 pool/ 下列出每个包一次,再列元数据和客户端入口。
它描述本地文件;目标进度与缓存处理由 sow publish 管理。
4 - 成员策略
用 Dist 策略排除不需要的包,并限制索引中的版本数量。在 sow.yml 中配置
exclude 和 limit:
SOW 先执行 exclude,再执行 limit。add、rm 和 build 都会对所选 Dist 的完整候选
成员集合应用策略。编辑策略后,先运行 sow config check,再运行 sow build,
使现有成员符合新规则。
排除软件包
每条规则包含一个或多个字段。规则内的所有字段都要匹配;一个字段内,任一模式匹配即可。 任一规则匹配时,SOW 排除该软件包。
此配置在所有架构上排除列出的包类型,同时排除名称以 test- 开头或以
-experimental 结尾的 aarch64 包。
| 字段 | 匹配的包属性 |
|---|---|
name |
二进制包名 |
source |
规范化的源码包名 |
arch |
x86_64、aarch64 或 neutral |
kind |
下表中的包类型 |
format |
rpm 或 deb |
模式区分大小写,支持精确字符串和 shell 通配符 *、?、[...],不支持正则表达式、
版本比较或取反。空规则、空模式、首尾有空白的模式、同字段重复模式和非法通配符都是配置错误。
SOW 根据二进制包名确定 kind:
| 格式 | 包名后缀 | kind |
|---|---|---|
| RPM | -debuginfo |
debuginfo |
| RPM | -debugsource |
debugsource |
| RPM | -llvmjit |
llvmjit |
| DEB | -dbgsym |
dbgsym |
| DEB | -dbg |
dbg |
| 任意 | 没有匹配后缀 | main |
用 sow show PACKAGE --json 查看计算出的类型。SOW 不会按此表校验模式值:
kind: [debug] 是合法配置,但不会匹配任何包类型。
限制版本数量
limit 按二进制包名和规范架构族分组。架构无关包(noarch 或 all)单独成组。
| 值 | 结果 |
|---|---|
0 |
保留所有版本,默认值 |
正整数 N |
每组保留最新的 N 个版本 |
| 负数 | 配置错误 |
RPM 使用原生 rpmvercmp 的 epoch-version-release 排序(包含 ~ 预发布与 ^ 后置快照算符,2.0~rc1 < 2.0 < 2.0^20250611 < 2.0.1),DEB 使用原生 Debian 版本排序,从不按普通字符串排序。
limit: 2 时,向已有版本 1、2 的 Dist 加入版本 3,会在同一次操作中移除版本 1 的成员关系。
即使包名相同,x86_64 包与 noarch 包也属于不同分组。
读取策略结果
输入包被所有选中 Dist 排除时,整体状态为 excluded。各 Dist 的结果中,
excluded 表示命中排除规则,limited 表示超过版本数量限制。
策略排除不计为输入失败,也不会因此使命令失败。
一个 Dist 可以接受某个包,同时另一个 Dist 排除它。没有 Dist 接受新包时, SOW 不会为它保存一个无成员关系的包对象。
恢复已移除的版本
放宽策略不会恢复旧成员。提高 limit、删除排除规则或移除最新版本,只会改变当前及后续
候选包的处理结果。要恢复某个版本,请重新加入其包文件:
策略移除的是成员关系,不是包字节。已构建或保留的构建快照可能仍引用该包,使其继续留在包池。
用 sow gc 回收无引用对象;对已发布目标使用 sow gc TARGET。
预览移除结果
预览只报告成员移除和预期文件变化,不写入状态。把文件列表视为完整计划前,请检查 JSON 中
的 prediction_complete。--check 不能与 --skip、--timeout 或 --no-wait 同用。
5 - 签名模型
配置元数据签名,让客户端验证仓库索引。需要客户端验证 RPM 文件内嵌签名时, 另行配置 RPM 包签名。
签名对象
| 配置 | 签名内容 | 输出 | 客户端设置 |
|---|---|---|---|
signing.rpm.metadata |
RPM 索引清单 | repodata/repomd.xml.asc |
DNF repo_gpgcheck=1 |
signing.deb.metadata |
APT 发布清单 | InRelease、Release.gpg |
APT Signed-By |
signing.rpm.packages |
RPM 包 | 内嵌 OpenPGP 签名 | DNF gpgcheck=1 |
元数据签名不改变包字节。RPM 包签名会改变包字节及 SHA-256。 SOW 不给 DEB 包文件增加签名。
配置元数据签名
RPM 与 DEB 可使用相同或不同密钥。密钥带口令时,在 key 旁增加
passfile: /run/secrets/gpg-password,或将 SOW_GPG_PASSFILE 设置为口令文件路径。
先检查配置,再构建:
已配置签名的空 Dist 与非空 Dist 生成相同种类的签名文件。InRelease 的签名正文与
Release 一致。未配置密钥时,SOW 生成未签名索引。
Managed 的密钥与签名策略来自 sow.yml;--passfile PATH 可为本次执行覆盖口令来源,
build 同样支持。
密钥与口令引用
| 元数据密钥引用 | 含义 | 签名实现 |
|---|---|---|
keys/repo.asc |
相对于工作区根的 OpenPGP 密钥文件 | 进程内 |
file:///secure/repo.asc |
绝对路径的 OpenPGP 密钥文件 | 进程内 |
env://SOW_METADATA_KEY |
ASCII 装甲格式密钥、文件路径或 GPG 密钥标识 | 取决于变量值 |
agent://KEY_ID_OR_FINGERPRINT |
本地 GPG 环境可用的密钥 | 外部 gpg |
密钥文件支持 ASCII 装甲格式和二进制 OpenPGP 数据。GPG 密钥标识可以是 16 位 key ID,
或 40、64 位指纹。引用解析为 GPG 密钥标识时,使用外部 gpg 访问私钥;
显式指定口令文件后,通过 GPG loopback 模式非交互解锁。
签名 key 引用必须解析为一个主密钥。
trusted_keys 文件可以包含多个完整公钥,包括连续的 ASCII 装甲块。
SOW 读取整个密钥集合,拒绝格式错误或被截断的输入,不会仅使用第一个密钥而忽略后续内容。
口令来源优先级为 --passfile PATH、每个密钥的 passfile 配置、SOW_GPG_PASSFILE。
旧元数据配置的 passphrase 引用继续兼容,优先级与单个密钥配置相同;
同一密钥的两个字段不能都非空。三种签名设置都接受 passfile,包括 agent:// 密钥。
口令文件为非空 UTF-8 单行。SOW 只移除末尾一个 LF 或 CRLF,保留空格与制表符。 不搜索默认路径,所选来源不可用时直接失败。完整格式、相对路径基准、权限及恢复命令范围 见口令文件。
config show 显示引用和解析出的公钥身份,不显示私钥或口令内容。
config check 解锁元数据私钥文件并检查用途;外部 GPG 元数据密钥还会试签。
RPM 包签名只检查口令格式、工具和私钥是否可用,不会签包或证明口令正确,
详见 config check。
创建新元数据签名时,选中的签名密钥需要在发布时间和
当前时间都可用。历史验证使用保留的证书。
新 APT 签名使用 SHA-256。新的发布尝试要求两份 APT 签名都使用 SHA-256、SHA-384 或 SHA-512。
升级包含 SHA-1 元数据签名的工作区后,先运行 build,再开始新发布。
已有发布尝试继续使用其记录的文件。
配置 RPM 包签名
| 模式 | 行为 |
|---|---|
never |
原样保存输入字节 |
fill |
已有签名可由当前密钥或受信密钥验证时保留原包,其余包签名 |
always |
已由当前密钥签名的包保留原样,其余包重签 |
没有密钥时默认 never,有密钥时默认 fill。fill 和 always 都必须配置密钥。
包签名在私有副本上调用主机的 rpm --addsign 或 rpm --resign。
安装 rpm、gpg,并确保 rpm 使用的 GPG 环境中有对应私钥。
密钥引用只标识签名者,不会把私钥导入该环境。原始输入文件保持不变。
SOW 检查签名结果、包坐标及排除签名头后的包体摘要。已有对象的坐标和包体相同、
且符合当前策略时,可复用已有签名字节。never 模式要求字节完全相同才能复用。
包签名工作流请使用 RPM 格式 v4。SOW 支持验证 RSA、DSA、ECDSA 与 EdDSA RPM 签名,包括保存在 RPM 签名头 tag 267(RPMSIGTAG_DSA)中的纯头签名。SOW 不支持 RPM 格式 v6 使用的签名标签。
这与 OpenPGP 签名报文版本不同;SOW 支持历史 CentOS 的 OpenPGP v3、v4 签名。
更换密钥
只修改 passfile 路径不会让 Dist 变脏;签名身份变化会使相关 Dist 变脏。更换元数据密钥后,运行 build 生成新签名,
并在发布新元数据前把新公钥分发给客户端。
已有包池对象不可变,修改 RPM 包签名策略不会原地重签这些对象。 逐步轮换包签名密钥时:
- 将新密钥设为当前密钥,使用
mode: fill。 - 旧包仍为成员时,在
trusted_keys中保留旧公钥。 - 加入由新密钥签名的新版本包。
- 旧签名包退出成员集合或被新版本替代后,再移除旧信任。
直接换新密钥并使用 always,要求所有期望成员 RPM 已符合新密钥策略。
若重签会使同一包 URL 对应不同字节,应使用新的包发行版本和文件名。
RPM 验证按包的签名时间检查证书和签名子钥是否有效。证书保留了所需历史自签名与子钥绑定时, 续期本身不会使旧包签名失效。导出续期公钥时保留这些历史公钥信息。
Plain 签名
Plain 模式只签 RPM 包文件。不带 --overwrite 时,为没有可解析内嵌签名的包签名;
带该选项时,重签全部保留的 RPM。密钥标识为 16、40 或 64 位十六进制字符,
可带 0x 前缀。执行 --pigsty 清理后,至少需要剩余一个 RPM。
6 - 事务与恢复
Managed 模式先记录变更,再应用变更。中断后,SOW 根据记录完成或回滚操作。 应使用哪个命令恢复,取决于中断的操作。
恢复中断操作
先查看记录的状态:
| 状态 | 下一步 |
|---|---|
clean |
交付前运行 sow check |
dirty |
运行 sow build,应用待构建的成员或配置变化 |
recovering |
按下表选择与中断操作对应的恢复命令 |
error |
根据诊断与日志检查报错文件或操作 |
| 中断操作 | 恢复命令 |
|---|---|
add、rm、Dist 修改或构建 |
sow build -r demo |
init、repo new 或 repo rm |
重新运行对应的创建、删除命令 |
| 发布 | sow publish TARGET |
| 本地或目标垃圾回收 | sow gc -r demo 或 sow gc TARGET |
| 移除保留构建快照 | sow retain rm GENERATION -r demo |
| Plain 索引生成或 RPM 清理 | 使用原选项重新运行 sow create 或 sow prune |
SOW 按未完成状态(而非记录的时间戳)选择活动的仓库变更操作,因此系统时钟回拨不会把未完成操作隐藏在更早的已完成记录之后。本地或目标 gc 以及 retain rm 在固定执行计划后中断时,SOW 会保留该计划,再次运行同一命令即可完成中断的清理。
恢复过程如果需要重新生成带签名的元数据,就需要再次访问签名密钥与口令。SOW 不在日志中保存口令值。
可用 --passfile PATH、单个密钥配置或 SOW_GPG_PASSFILE 提供来源;支持版本和优先级见
口令文件。工作区生命周期日志尚待恢复时,用命令行或
环境变量覆盖口令来源,保持日志记录的 sow.yml 不变,其摘要属于恢复校验的一部分。
先修正报错中的输入、配置或存储问题。恢复期间,工作区配置、数据库和私有状态应保持配套。
本地恢复完成后,运行 sow check -r demo。常见错误见故障处理,
发布恢复见 publish。
SOW 记录什么
| 记录 | 位置 | 用途 |
|---|---|---|
| 工作区生命周期日志 | .sow/workspace-ops/active.json |
协调配置与仓库创建、删除 |
| 仓库操作日志 | .sow/<repo>.db |
记录本地成员变更、构建与维护 |
| 发布状态 | .sow/<repo>.db |
记录目标绑定、固定的发布计划和进度 |
工作区生命周期恢复会比较当前 sow.yml 的摘要与日志中的新旧配置摘要。
配置替换决定该生命周期变更应回滚还是完成。两者都不匹配时,报告完整性错误。
仓库成员变更使用自己的状态转换:
| 状态 | 含义 |
|---|---|
planned |
SOW 已记录操作及其范围 |
staged |
SOW 已为下一阶段准备输入 |
applied |
SOW 已提交期望状态,公开目录可能仍使用上一构建快照 |
built |
SOW 已安装生成的公开目录 |
done |
操作完成 |
done_dirty |
SOW 已提交期望状态,仍需构建 |
其他终态包括 rolled_back 和 failed。用 sow log OPERATION -r demo 查看转换和时间。
--skip 通常以 done_dirty 结束。已识别的签名策略变化、构建计划固定前的配置变化,
或受支持的内部契约升级,也可能让已提交操作保留为脏状态。日志会记录原因。
需要时修正配置,再次运行 build。
已提交变更后的错误
数据库提交与后续维护是两个步骤。成员关系或构建已经提交后,仍可能发生检查点或清理错误。 SOW 会在命令输出中保留已提交结果,不会把这些项目报告为已回滚。
先查看命令结果与 sow status,再用 sow log 查看对应操作。
按诊断提示继续执行相应命令。确认结果前,保留原始输入文件。
本地构建顺序
SOW 在同一文件系统暂存并检查输出,再按以下顺序应用:
- 把包字节放入规范
pool/路径。 - 安装按校验和命名的 RPM 元数据,以及 APT 直接索引和 by-hash 索引。
- 安装 RPM 的
repomd.xml和已配置的签名,或适用的 APTRelease、InRelease、Release.gpg。 - 替换持久化后,清理过时元数据。
构建保留紧邻前一代的 RPM 不可变元数据和 APT by-hash 对象,供仍持有旧入口的客户端读取。
显式构建快照保留是独立功能,详见 retain。
脏仓库仍服务最近完成的构建快照。多 Dist 操作中的各视图可能在不同时间切换。
远端 HTTP 请求和缓存也有各自的存续时间;在线目标更新应使用 sow publish。
sow changes 只列出本地文件变化,不执行发布或目标清理。
锁与等待
SOW 通过建议锁协调进程。工作区生命周期使用 .sow/workspace.lock,
仓库写操作使用 .sow/repo-locks/<repo>.lock。需要两把锁时,先取工作区锁。
需要一致快照的读命令使用共享锁。status 探测仓库写锁,无需等写者结束即可报告活动状态。
Plain create 与 prune 锁住目标目录及其父目录。
| 选项 | 行为 |
|---|---|
-T 30s |
最多等待写锁 30 秒 |
-T 0 |
等待到锁可用,默认行为 |
-N |
只尝试一次,其他写者占锁时立即退出 |
无法取得锁时退出码为 4。-N 与非零 -T 同用时退出码为 2。
只使用命令支持的锁选项;只读命令不接受这些选项。
文件系统要求
使用支持建议锁、fsync 和原子重命名的本地 POSIX 文件系统,macOS 使用 APFS。
公开文件与私有暂存区应位于同一文件系统。SOW 拒绝控制路径中的符号链接、
非预期文件类型和越界路径,并在替换或删除受控对象前检查文件身份。
网络文件系统不适合作为工作区。
仓库与 Dist 名称使用 [a-z0-9][a-z0-9._-]*,保留名称见
仓库布局。脚本如何处理中断、占锁和完整性错误,
见退出码。
7 - 可观测与审计
用 status 查看状态,check 校验仓库,changes 查看文件变化,log 读取操作历史。
| 命令 | 回答的问题 |
|---|---|
status |
是否需要构建或恢复?是否有写者活动? |
check |
状态、包字节、索引、签名是否一致且可以交付? |
changes |
两个构建快照之间有哪些本地文件变化? |
log |
执行了哪些操作,改变了什么? |
retain ls |
显式保留了哪些构建快照? |
这些命令不修改仓库内容。
查看状态
结果包含期望状态修订号、构建快照、脏 Dist、待构建包的数量与字节数、写锁状态及 ready_to_copy。
| 状态 | 含义 |
|---|---|
clean |
期望状态与构建状态一致 |
dirty |
成员或配置变化需要构建 |
recovering |
已记录工作需要恢复或清理 |
error |
记录或文件证据存在未解决错误 |
status 报告状态,不计算公开文件哈希。dirty 状态运行 build;
recovering 或 error 状态按诊断及恢复流程处理。
交付前运行 check。
校验仓库
正常检查报告九层:
| 检查层 | 内容 |
|---|---|
config |
配置与生效 Dist 设置 |
state |
SQLite schema 与关系状态 |
public-modes |
公开文件和目录权限 |
retained |
保留构建快照记录与元数据 |
package-bytes |
包池和待构建包字节是否匹配记录的 SHA-256 |
desired-membership |
包身份、架构和成员关系 |
index |
索引内容和引用文件 |
signature |
已配置的元数据与 RPM 包签名 |
generation-manifest |
构建文件清单与公开目录是否一致 |
布局转换未完成时,改为报告 config、state、public-modes、layout-transition。
按 repo migrate 的说明完成或中止迁移。
check 忽略缓存指纹,读取每份唯一物理包并计算哈希,不修复文件。
即使上一构建快照仍一致,脏仓库或恢复中的仓库也不能通过交付检查。
比较构建快照
不传基线时与上一构建快照比较;基线 0 列出当前完整目录;基线 42 与已记录构建快照 42 比较。
各行按适用情况包含动作、阶段、仓库相对路径、大小和 SHA-256。
该比较不检查发布目标。用 sow publish TARGET 更新目标并继续中断传输,
用目标 GC 回收远端对象。
保留构建快照与回收无用包
retain add 只接受当前构建快照。若 status 显示构建快照为 42,应在后续构建替换它之前保留:
命令校验该构建快照,保存元数据与引用;包文件仍由共享包池保存。
不再需要构建快照 42 时,用 sow retain rm 42 -r demo 移除保留记录,
再用 sow gc -r demo 回收无用字节。若 retain rm 或 gc 中途中断,重新执行同一命令即可继续完成已记录的计划。本地 gc 要求仓库为 clean;有待构建变更时先构建。
期望成员、已构建成员、为旧客户端保留的成员记录,以及保留构建快照、恢复或发布状态
仍需使用的包都会保留。完整规则见 gc。
普通构建保留的前一代元数据与显式保留功能相互独立。
目标清理使用 sow gc TARGET:filesystem 目标在缓存宽限期后支持条件删除,
R2 目标只报告候选,不执行删除。
查看与导出日志
日志记录操作状态、时间、包处理结果,以及适用的成员与文件变化。 操作详情还可能包含构建进度事件。
log export 将 JSON Lines 写入新文件或 stdout。log prune 接受日期或 RFC 3339 时间戳,
删除符合条件的终态审计记录。恢复所需记录和构建快照清单会保留。