跳转到主要内容

sow create

在普通目录中就地生成平面 RPM/DEB 仓库 —— Plain 平面模式的唯一入口。

sow create 把一个已经放着 .rpm / .deb 的目录变成平面仓库(flat repository):在包旁边写出索引 文件。它就是 Plain 平面模式的全部——没有 sow.yml、没有 SQLite、不做工作区发现。本页讲清单遍扫描 契约、RPM 发布时间、--pigsty 完成门禁,以及 --sign-with 的 RPM 包签名。

版本要求

--metadata-timestamp 要求 SOW 0.5.0 或更新版本。 切换已有维护脚本前,请检查实际二进制的 create --help。

语法

sow create [DIR] [-j N] [--metadata-timestamp SECONDS] [--pigsty] [-S KEY [--overwrite]] [-T DUR | -N] [--json]

DIR 默认为当前目录。

说明

create 读取 DIR 顶层的普通文件,按发现的内容渲染对应索引:有 RPM 就生成 repodata/,有 DEB 就 生成 Packages 与 Packages.gz,混合目录两套一起生成。架构全部来自包头——Plain 模式没有架构参数, 也没有架构许可表。

平面元数据只引用同目录的包:RPM 的 location 是裸 basename,DEB 的 Filename 是 ./<basename>。无论目录作为 file:// 源还是 HTTP 根暴露,两者都保持相对引用。

默认情况下 create 不删除、不移动、不重命名、不重签、不改写任何一个包字节。它只替换自己拥有的索引 路径,未知文件原样保留。

参数

参数 说明 默认
-j, --jobs N 唯一一次包哈希/解析扫描的并发 worker 数 逻辑 CPU 数
--metadata-timestamp SECONDS RPM repomd 的 data 时间戳,取值 0 至 253402300799 的 Unix 秒;不影响 DEB、包时间或 gzip 头 0
--pigsty 启用 Pigsty 兼容清理与完成 marker 关闭
-S, --sign-with KEY 用 16/40/64 位十六进制 GPG key ID 给未签名 RPM 补签 关闭
--overwrite 重签全部 RPM;必须与 --sign-with 同用 关闭
-T, --timeout DUR 等待锁的最长时间;0 表示无限等待 0
-N, --no-wait 锁被占用时立即失败 false
--json 输出版本化 JSON envelope false
-h, --help 显示帮助 —

扫描规则

  • 只考虑顶层、以 .rpm 或 .deb 结尾的普通文件。
  • 不递归、不跟随符号链接、不读工作区配置。
  • 所有有效版本都进入索引。两个文件对应同一逻辑坐标但内容不同时,硬失败。
  • 默认模式下没有受支持包会被拒绝;--pigsty 接受空权威集合,以便中断的“删除全部包”清理能够收敛并写 marker。
sow create /srv/empty
plain: scan /srv/empty: no supported top-level regular RPM or DEB packages

包 I/O 与最终校验

默认未签名路径中,每个选中包恰好只有一次完整内容扫描。worker 打开包、计算一次 SHA-256、解析 header/control,并保留完整解析结果。RPM XML 与 DEB Packages 都从该结果渲染;渲染和生成元数据 校验都不会重新打开包体。--jobs 并行化这一次扫描,规范结果顺序保证 worker 调度不改变输出字节。

发布前,create 重新列出顶层包集合,把文件 identity、类型/mode、size 与 mtime 同扫描后快照比较。 这是便宜的 stat 校验,不是第二次哈希。集合或 stat 变化会在任何 stage 输出发布前以完整性错误 5 退出。原地改字节同时刻意保持 inode、size、mtime 不变,不属于本机协作写者契约。

显式 RPM 签名是例外:复制、签名、签名验证以及解析最终签后 RPM,会对实际修改的包增加必要读取。

确定性输出与幂等

不强制重签软件包时,对给定输入集和显式时间参数,渲染出的元数据是字节稳定的:gzip 输出确定,repomd.xml 写 <revision>0</revision>,data timestamp 默认为 0,可用 --metadata-timestamp 显式指定。使用相同参数重跑未变化的目录不会改写文件,报告 noop=true:

sow create /srv/flat
created /srv/flat: rpm=3 deb=1 signed=0 removed=0 marker=false noop=false recovered=false

sow create /srv/flat
created /srv/flat: rpm=3 deb=1 signed=0 removed=0 marker=false noop=true recovered=false

已有 YUM 仓库的发布时间

EL7 的 YUM 会拒收 data 最大时间比缓存更早的 repomd。因此迁移已有在线仓库时,不要使用默认的 0;给本次发布指定一个不低于历史最大值的时间,真实内容更新建议严格递增。EL7 接受相等时间;迁移指南建议内容变化时至少加一秒,是为了区分各次发布:

每次更新都要传入 --metadata-timestamp;省略时仍使用 0,不会继承旧值。 接受范围为 0..253402300799 Unix 秒(截至 9999 年)。当代毫秒纪元值会因超出范围而被 拒绝;SOW 不会自动辨认较小数值的单位。这是输入范围,不代表所有客户端都支持任意日期。 过大的未来时间可能使 EL7 拒绝随后正常时间的索引,直到清理旧元数据缓存。repomd 变化后,必须在发布前 重新生成并替换 .asc 签名;create 不会更新或删除旧 repomd.xml.asc。

sow create /srv/flat --metadata-timestamp 1790049000

这里的数字是示例,生产值由维护流程选择并记录。只修改 revision 或文件 mtime 无效。参数仅控制 RPM repomd 的 data 时间,不改包字节、三份 XML.gz 或 DEB 索引;不能代替 repomd 的 GPG 签名。改动 repomd 后,调用者应重新签署 .asc,SOW create 不负责仓库入口签名。

SOW 不自动读取当前时钟、历史备份或远端缓存;维护包装应比较真实索引内容,无变化时复用原 repomd 与有效签名,真实更新时使用 max(当前秒, 历史高水位+1)。第一次从 timestamp=0 修复时,要把可信旧备份的时间纳入下限。恢复旧包集合也属于新发布,不应把索引时间倒退。

该参数仅用于 plain create,不改变 managed build 或 RPM leaf export 的时间语义。

备份、发布时间记录、签名与带缓存客户端验收的完整流程,见迁移现有 YUM 仓库。

repo_complete 门禁

默认模式永不生成 repo_complete。如果 marker 已经存在,create 宁可拒绝写索引,也不留下一个内容 已过期却仍宣称"完成"的旧 marker:

sow create /srv/pigsty
plain: marker gate /srv/pigsty/repo_complete: repo_complete exists; use --pigsty or remove it explicitly before rebuilding

要么加 --pigsty 重跑(由它按文档顺序撤下并重新发布 marker),要么自己先把 marker 移走。

–pigsty

--pigsty 在一次调用中同时启用三项相互关联的兼容动作。发布顺序受 marker 门禁保护,但中断后是 重新扫描重建,不会从 journal 恢复:

  1. 删除解析架构为 i386 的 DEB;RPM 不会仅因为架构是 i386/i486/i586/i686 而被删除。
  2. 删除二进制包名恰为 patroni 且 upstream 版本恰为 3.0.4 的 RPM/DEB。RPM 比较 VERSION,忽略 epoch 与 release;DEB 先剥掉 epoch 与 Debian revision 再比。3.0.4+foo 不算命中。
  3. 全部索引渲染成功后写出 repo_complete:剩余顶层 RPM/DEB 的 SHA-256,按 basename 字节序排序, 格式为 <sha256><两个空格><basename>。
sow create /srv/pigsty --pigsty
created /srv/pigsty: rpm=2 deb=0 signed=0 removed=2 marker=true noop=false recovered=false
cat /srv/pigsty/repo_complete
b4111ef2a51542eacc9bd1ebd080da02e53d400f9d172530c75a1e4ac06e7ead  centos-release-7-2.1511.el7.centos.2.10.x86_64.rpm
d6f332ed157de1d42058ec785b392a1cc4b5836c27830af8fbf083cce29ef0ab  epel-release-7-5.noarch.rpm

清理只触碰解析成功且命中规则的顶层普通包文件,绝不按宽泛 glob 删目录或未知文件。

发布顺序对以 marker 为门禁的调用方很关键:先撤下已有的 repo_complete,再切换索引,只在替换 元数据安装后删除命中包,最后才写入新 marker。调用方必须把 marker 缺失视为尚未完成。

Marker 语义

把 repo_complete 缺失当作"构建进行中"。这正是 --pigsty 设计围绕的契约。

RPM 包签名

-S/--sign-with KEY 是修改 RPM 字节的显式授权。KEY 必须是恰好 16、40 或 64 位十六进制 GPG key ID/fingerprint,接受可选的 0x 或 0X 前缀。SOW 将其规范化为大写,通过 _gpg_name macro 传给环境中的 rpm --addsign。私钥、passphrase、GPG home、pinentry 以及额外 RPM macro 都由你的运行环境提供—— SOW 不接收、不持久化、不回显任何秘密。

  • 默认只给没有可解析嵌入 OpenPGP 签名的 RPM 补签;已有签名的包保持原字节。
  • --overwrite 必须与 --sign-with 同用,改为对全部保留 RPM 执行 rpm --resign。
  • 签名发生在同文件系统的私有 stage 副本上。每个结果都会重新解析以确认嵌入签名存在、 signature-neutral digest 与 NEVRA 未变,并以最终完整字节生成 rpm-md。
  • --pigsty 清理后至少要保留一个顶层 RPM,且 PATH 中要有 rpm。
sow create /srv/flat -S 0123456789ABCDEF --overwrite
plain: sign rpm epel-release-7-5.noarch.rpm: rpm executable is required for --sign-with
sow create /srv/deb-only -S 0123456789ABCDEF
plain: sign rpm: --sign-with requires at least one retained top-level RPM package
sow create /srv/flat --overwrite
usage error: --overwrite requires --sign-with
sow create /srv/flat -S ZZZZ
usage error: --sign-with must be a 16, 40, or 64 hexadecimal GPG key ID/fingerprint

锁、staging 与覆盖重建

create 对目标目录取写锁,服从 --timeout/--no-wait。全部元数据先写入私有 stage 并验证,之后 才开始发布。锁协调本机 SOW 写者;任意外部进程同时修改包不属于受支持负载。

Plain create 不创建持久操作 journal、回滚 pre-image 或 recovery trash。发布由多个单文件 rename 组成, 因此崩溃可能留下部分替换的派生文件。使用你当前想要的参数重新执行 sow create:它丢弃保留命名空间 中的陈旧 Plain 临时状态,再按现在仍存在的包重建全部索引。recovered 始终为 false; 重跑是一次全新覆盖构建,不是事务重放。

平面目录没有整个仓库的 generation 指针,RPM 与 DEB 入口也无法用一次 POSIX rename 同时切换。因此 Plain 不承诺跨文件瞬时原子性。--pigsty 用 repo_complete 做门禁;需要事务恢复时使用 Managed。

示例

给混合目录建索引:

sow create /srv/flat
created /srv/flat: rpm=3 deb=1 signed=0 removed=0 marker=false noop=false recovered=false
ls /srv/flat
centos-release-6-0.el6.centos.5.x86_64.rpm
centos-release-7-2.1511.el7.centos.2.10.x86_64.rpm
epel-release-7-5.noarch.rpm
libpq5_18.3-1_amd64.deb
Packages
Packages.gz
repodata

机器可读结果:

sow create /srv/flat --json
{"schema":"sow.cli/v1","command":"create","ok":true,"repository":null,"operation":null,"result":{"dir":"/srv/flat","rpm":3,"deb":1,"kept":["centos-release-6-0.el6.centos.5.x86_64.rpm","centos-release-7-2.1511.el7.centos.2.10.x86_64.rpm","epel-release-7-5.noarch.rpm","libpq5_18.3-1_amd64.deb"],"removed":[],"marker":false,"noop":true,"recovered":false},"errors":[]}

用八个 worker 构建 Pigsty 平面仓库,并使用根据发布历史选定的时间:

sow create /www/pigsty -j 8 --pigsty --metadata-timestamp "${PUBLISH_TIME:?Set the verified publication time}"

调用者负责记录该时间,并在客户端要求时为生成的 repomd.xml 签名。

失败时的 envelope:

sow create /srv/empty --json
{"schema":"sow.cli/v1","command":"create","ok":false,"repository":null,"operation":null,"result":null,"errors":[{"code":6,"class":"rejected","message":"operation rejected: plain: scan /srv/empty: no supported top-level regular RPM or DEB packages"}]}

退出码

码 触发条件
0 索引写出成功,或输入未变化产生 no-op
1 目录不可读或不存在、包解析失败、渲染失败、签名工具失败
2 用法错误——时间戳非法或重复指定、--overwrite 未配 --sign-with、key 格式非法、--no-wait 与非零 --timeout 同用
4 目录写锁被占用,且给了 --no-wait 或 --timeout 到期
5 发布前输入集合/stat 变化,或受控输出路径未通过完整性检查
6 未找到受支持的包、撞上 repo_complete 门禁、对 DEB-only 目录用 --sign-with、坐标冲突

参见