跳转到主要内容

这是本节的多页打印视图。 .

返回本页常规视图.

参考

查阅配置、包引用、文件路径、命令结果与故障处理方法。

这些页面用于查阅配置字段、包引用、文件路径和脚本接口。 命令语法见命令参考,也可运行 sow help COMMAND。

示例约定

命令代码块省略 shell 提示符,便于复制。语法说明中的 NAME、DIR 等大写词是占位符。 方括号表示可选参数,... 表示可重复参数,| 分隔备选项。

输出中的路径、ID、摘要、时间和计数随工作区变化。操作 ID 和构建快照 ID 保持字符串类型, 详见 JSON 输出。

1 - 故障处理

处理数据库迁移、待构建变更、中断发布、锁等待和完整性错误。

先查看报错信息和退出码。 运行 Managed 命令时,明确指定工作区与仓库,检查对应状态:

sow version
sow status -C /srv/sow -r pgsql
sow log -C /srv/sow -r pgsql

将以下示例中的 /srv/sow、pgsql 和目标名 prod 换成实际配置。

数据库需要迁移

SOW 0.7.1 与 0.5.0 至 0.7.0 均使用 schema v13。从这些版本升级均无需迁移数据库。 对于 0.3 或 0.4 工作区,先停止写入并备份完整工作区,再运行:

sow repo migrate pgsql -C /srv/sow
sow build -C /srv/sow -r pgsql
sow check -C /srv/sow -r pgsql

对每个仓库重复这些步骤。兼容的只读命令仍可检查旧 schema,但不会自动迁移数据库。 如果由 SOW 0.5 或 0.6 从 C2 布局迁移的仓库在后续构建后报告迁移锚点目标无效,升级到 SOW 0.7.1 即可自动恢复,无需手工修复。 详见升级说明。

如果 C2 迁移暂存残留阻塞本地 GC,重新运行 sow repo migrate REPOSITORY。 SOW 0.7.1 会将无法证明可安全清理的文件保留到 .sow/<repo>/abandoned/ 并报告路径,删除前请先检查。

状态显示有待构建变更

add --skip、rm --skip 和配置修改可能使 Desired 状态领先于 Built 状态。 应用待构建变更,然后检查结果:

sow build -C /srv/sow -r pgsql
sow check -C /srv/sow -r pgsql

本地构建更新本地索引。要更新已配置的远端目标,还需执行 sow publish prod -C /srv/sow。 状态字段见 sow status。

命令执行中断

Plain 模式下,解决报错中的输入或文件系统问题后,重新运行原来的 sow create 命令(保留原定的 Pigsty、时间戳和签名选项),或重新运行原来的 sow prune 命令(保留相同的 --protect 和 --metadata-timestamp 选项)。

add、rm、Dist 修改或构建中断时,查看 sow log,修正报错原因,再对该仓库运行 sow build 完成恢复。 已提交的 Desired 变更仍属于待构建内容。init、repo new 或 repo rm 中断时,重新运行对应的生命周期命令,恢复工作区日志。 本地 gc 或 retain rm 中断时,重新运行 sow gc 或 sow retain rm GENERATION;SOW 0.7.1 会继续执行已持久化的计划,或丢弃在删除开始前中断的空计划。

恢复需要带口令的签名密钥时,应再次提供口令来源,日志不会保存口令。支持该选项的恢复命令 可使用 --passfile PATH 或 SOW_GPG_PASSFILE,单个密钥配置仍然适用。 sow.yml 必须与待恢复的工作区生命周期日志保持一致,详见口令文件。

发布中断时,恢复存储和公共端点的访问,再次发布同一目标:

sow publish prod -C /srv/sow

SOW 会继续保存的发布尝试。尚未记录提交意图时,可用 sow publish prod --abort -C /srv/sow 放弃该次发布。已记录提交意图后,重新发布以完成更新。详见发布恢复。

已提交变更后的错误

数据库提交与后续维护是两个步骤。成员关系或构建已经提交后,仍可能发生检查点或清理错误。 SOW 会在命令输出中保留已提交结果,不会把这些项目报告为已回滚。

先查看命令结果与 sow status,再用 sow log 查看对应操作。 按诊断提示继续执行相应命令。确认结果前,保留原始输入文件。

发布目标配置发生变化

比较 sow.yml 与报错中的目标。如果有意修改目标名称、public_endpoint 或 max_cache_ttl,运行:

sow publish prod --rebind -C /srv/sow

Repository、后端、存储端点、区域、桶或前缀变化时,需要新建目标。 修改 TTL 前,先完成未结束的目标维护。详见目标绑定。

命令等待锁

另一个进程正在使用工作区或仓库。等待该命令结束,或通过正常的进程控制方式停止它,然后重试。

对于支持锁选项的写命令,锁忙时需要立即返回可用 --no-wait,需要限制等待时间可用 --timeout 30s。 这些选项不适用于 init 或读命令。共享锁读操作会等待写入者结束,或由你取消命令;status 通常直接报告锁状态。保留锁文件,SOW 依靠这些稳定路径协调进程。 详见全局选项。

软件包、签名或路径校验失败

签名密钥要求输入口令或无法解锁

使用支持口令文件的二进制时,按优先级选择 --passfile PATH、单个密钥的 passfile、SOW_GPG_PASSFILE。create 还需 --sign-with,仅设置环境变量不会启用签名。

  • 先检查最终选中的路径。命令行和环境变量中的相对路径以进程当前目录为基准, 不受 -C 影响;配置中的相对路径以工作区根为基准。环境变量为空与未设置不同。
  • 使用可读、无符号链接的普通文件。内容为非空 UTF-8 单行,可以带一个末尾 LF 或 CRLF; 空格和制表符都属于口令。文件不是 shell 脚本,也不是 NAME=value 赋值。
  • 所选来源不可用时直接失败,不回退或弹出输入框。外部 GPG 需要 agent 支持 loopback; 硬件密钥仍可能要求自身的交互确认。
  • config check 解锁元数据密钥,并对外部元数据 GPG 做试签。RPM 包签名前置检查不能 证明口令正确,应验证一次实际签包操作;check 只验证公开签名,不读取口令。

其他校验失败

根据报错中的文件和检查阶段定位原因:

错误 处理方法
同一 Pool 路径对应不同字节 比较输入包与已有包。不同内容使用新包版本和新文件名。仅换输入目录不会改变 Pool URL。
文件名仅大小写不同 使用已记录的拼写。大小写别名不能创建第二个 Pool URL。
Pool 文件丢失或被修改 从匹配的备份恢复原字节,再运行 sow check。
签名密钥不可用 检查配置的密钥、私钥访问、GPG agent 和当前密钥有效期,修正后重试构建。
RPM 不满足信任策略 检查包签名与配置的公钥。详见签名教程。
公共 URL 返回旧内容 检查 HTTP 根目录、URL 前缀、缓存和 max_cache_ttl,然后重试发布。

sow check 报告完整性错误,不会改写损坏数据。排查时保留报错路径与操作 ID。 目录布局介绍包池、生成索引和私有状态各自保存的文件。

2 - sow.yml 配置参考

工作区配置字段、默认值、校验规则、签名引用与发布目标。

sow.yml 配置 Managed 工作区,声明仓库、Dist、包策略、签名与发布目标。 Plain 命令 sow create 与 sow prune 不读取此文件。

编辑后,检查配置并查看实际生效的设置:

sow config check
sow config show --all

config check 校验 YAML、比较配置与已初始化的仓库状态,并解析签名引用。 config show --all 展开默认值。

文件规则

  • 使用一个 YAML 文档,配置 schema: sow/v3。
  • 未知字段和重复键都是错误。
  • 文件放在工作区根目录,必须是只有一个硬链接的普通文件,大小不超过 16 MiB。 不接受符号链接。
  • init、repo new/rm、dist new/rm 在执行相应操作时更新此文件。
  • 策略、签名和目标设置直接编辑文件。读取时应用默认值,不改写源文件。

旧仓库数据库或目录布局的升级方法见 repo migrate。

工作区

schema: sow/v3
architectures: [x86_64, aarch64]
repos: {}
targets: {}
字段 类型 默认值 含义
schema 字符串 必填 必须为 sow/v3
architectures 字符串列表 [x86_64, aarch64] 工作区允许的架构族
repos 映射 空 仓库名称和设置
targets 映射 空 发布目标名称和设置

架构别名按下表规范化:

输入 规范架构族
x86_64、amd64 x86_64
aarch64、arm64 aarch64

显式架构列表不能为空,规范化后不能有重复族。noarch 和 all 是包架构, 不能写入配置架构列表;SOW 会将这些架构无关包加入各个适用视图。

仓库

repos:
  demo:
    protected: false
    signing: {}
    dists:
      el9:
        format: rpm
字段 类型 默认值 含义
protected 布尔值 false 阻止 repo rm,包括带 -f 的调用
signing 映射 元数据不签名;包模式为 never 仓库签名设置
dists 映射 空 Dist 名称和设置

protected 不阻止包变更或 Dist 删除。要删除受保护仓库,先设为 false, 检查配置,再运行 repo rm。

仓库、Dist 和目标名称必须匹配 [a-z0-9][a-z0-9._-]*。 保留名称为 .、..、.sow、pool、dists、sow.yml、workspace.lock、 workspace-ops、repo-locks。仓库的私有状态路径也不能冲突,例如 db 与 db.db 不能共存。

Dist

dists:
  el9:
    format: rpm
    architectures: [x86_64]
    limit: 2
    exclude:
      - kind: [debuginfo, debugsource, llvmjit]
字段 类型 默认值 含义
format 字符串 必填 rpm 或 deb,初始化后不可改变
architectures 字符串列表 工作区列表 工作区架构族的非空子集
limit 整数 0 每个二进制包名、规范架构分组保留的最新版本数;0 为全部保留
exclude 规则列表 空 包排除规则

增加架构会使 Dist 变脏,运行 build 生成视图。 成员关系或构建状态仍引用某个架构时,不能移除该架构。

limit 不能为负数。RPM 使用 epoch-version-release 排序(插入符 ^ 快照版本排在正式修订版本之前,如 2.0^20250611 < 2.0.1),DEB 使用 Debian 版本排序。 架构无关包单独成组。放宽策略不会恢复已移除成员,需要重新加入包文件。

排除规则

exclude:
  - kind: [debuginfo, debugsource, dbgsym, dbg, llvmjit]
  - name: ["test-*", "*-experimental"]
    arch: [aarch64]

规则内字段按 AND 组合,字段内模式按 OR 组合,规则之间按 OR 组合。 每个所选 Dist 的完整候选成员集合先执行 exclude,再执行 limit。

字段 匹配对象
name 二进制包名
source 规范化的 RPM SOURCERPM 或 DEB Source 名称
arch x86_64、aarch64 或 neutral
kind RPM:debuginfo、debugsource、llvmjit;DEB:dbgsym、dbg;其他为 main
format rpm 或 deb

模式区分大小写,支持精确字符串或 shell 通配符(*、?、[...])。 空规则、空模式、首尾空白、同字段重复模式和非法通配符都会报错。 模式值不会按可能的包属性值校验,拼错的值可能匹配不到任何包。 kind 由对应的包名后缀确定。

示例和结果解释见成员策略。

签名

签名设置属于仓库。RPM 包、RPM 元数据、DEB 元数据分别配置:

signing:
  rpm:
    packages:
      mode: fill
      key: agent://7F721C4AD40F4A9D8CA578BFAC7E4690B50CCF3B
      passfile: /run/secrets/rpm-password
      trusted_keys: [keys/upstream.asc]
    metadata:
      key: keys/repo-signing.asc
      passfile: /run/secrets/metadata-password
  deb:
    metadata:
      key: keys/repo-signing.asc
      passfile: /run/secrets/metadata-password

RPM 包设置

rpm.packages 字段 默认值 含义
mode 无密钥时 never,有密钥时 fill never、fill 或 always
key 无 签名密钥引用,fill、always 必填
passfile 无 口令文件路径,需要同时配置 key
trusted_keys 空 fill 接受的额外公钥引用

never 保留输入字节;fill 保留可由当前密钥或受信密钥验证的包,其余包签名; always 要求当前密钥,必要时重签。key 的公钥部分自动受信。 重复的受信密钥引用字符串会被拒绝。

包签名对暂存副本调用主机 rpm 命令,其 GPG 环境中必须已有对应私钥。 仅配置一个密钥文件不会为该环境导入私钥。

元数据设置

rpm.metadata 与 deb.metadata 都接受:

字段 默认值 含义
key 无 元数据签名密钥引用
passfile 无 口令文件路径,需要同时配置 key
passphrase 无 兼容旧配置的口令引用,与 passfile 互斥

RPM 签名生成 repodata/repomd.xml.asc,DEB 签名生成 InRelease、Release.gpg。 没有密钥时索引不签名。DEB 包文件不会被重签。

密钥引用

形式 示例 内容
路径 keys/repo.asc OpenPGP 密钥文件;相对路径从工作区根开始
file:// file:///secure/repo.asc OpenPGP 密钥文件
env:// env://SOW_METADATA_KEY 环境变量保存 ASCII 装甲格式密钥、文件路径或 GPG 密钥标识
agent:// agent://7F721C4AD40F4A9D8CA578BFAC7E4690B50CCF3B 本地 GPG 环境中的密钥

环境变量名称使用 [A-Za-z_][A-Za-z0-9_]*。GPG 密钥标识可以是 16 位 key ID, 或 40、64 位指纹,十六进制字母不区分大小写。

密钥文件支持 ASCII 装甲格式和二进制 OpenPGP 数据,必须是不经过符号链接的普通文件, 大小不超过 16 MiB。相对路径从工作区根开始解析。 提供私钥内容时,元数据签名在 SOW 进程内完成;引用解析为 GPG 密钥标识时,调用外部 gpg, 无论该标识来自 agent:// 还是环境变量。

签名 key 引用必须解析为一个主密钥。 trusted_keys 文件可以包含多个完整公钥,包括连续的 ASCII 装甲块。 SOW 读取整个密钥集合,拒绝格式错误或被截断的输入,不会仅使用第一个密钥而忽略后续内容。

口令文件

此接口需要 SOW 0.7.0 或更新版本。已安装的二进制是否支持,以 sow help create 或 sow help config check 为准。

RPM 包、RPM 元数据与 APT 元数据统一使用口令文件接口,支持加密私钥文件与 agent:// 密钥。每个签名密钥分别按以下优先级选择来源:

  1. --passfile PATH:覆盖本次执行用到的所有签名密钥。
  2. 该密钥配置的 passfile,或旧配置中的 passphrase 引用。
  3. SOW_GPG_PASSFILE:未单独配置口令来源的密钥使用此默认文件路径。
  4. 未设置来源时,沿用现有密钥/GPG/RPM 口令处理方式。

passfile 与 key 同级,放在 repos.<name>.signing.rpm.packages、 signing.rpm.metadata 或 signing.deb.metadata 下,不是顶层配置。 YAML 中的空值 passfile: "" 视为未设置。命令行覆盖不会跳过配置校验: 同一元数据密钥的 passfile 与 passphrase 都非空时仍然报错。

passfile 接收普通文件路径,不接收 URL 或环境变量名称。命令行和环境变量中的相对路径 以进程当前目录为基准,-C 不会改变这个基准;配置中的相对路径以工作区根为基准。 SOW 不展开配置或环境变量值中的 ~、$VARIABLE;命令行参数可由 shell 提前展开。 不自动搜索默认文件。需要跨命令复用时,将 SOW_GPG_PASSFILE 设置为绝对路径。 需要凭据且选中了环境变量来源时,空 SOW_GPG_PASSFILE 会报错;命令行或单个密钥配置 可以覆盖它。恢复原有行为应取消设置该变量。

没有配置来源时,加密的元数据私钥文件仍需提供口令引用。外部元数据 GPG 可使用 agent 已解锁的密钥,但禁用 pinentry;RPM 沿用现有 agent 和宏配置行为。 未加密密钥无需口令文件。环境变量只提供口令来源,Plain create 仍需 --sign-with 才会启用签名。

sow create ./packages -S 0123456789ABCDEF --passfile /run/secrets/gpg-password
export SOW_GPG_PASSFILE=/run/secrets/gpg-password
sow config check
sow build -r demo

文件直接保存口令,格式规则如下:

  • 单行、非空、UTF-8 编码,不带 BOM;含可选末尾换行在内,最多 64 KiB。
  • 可以没有末尾换行;恰好移除末尾的一个 LF 或 CRLF。
  • 不做 trim:首尾空格和制表符都是口令的一部分。引号、#、反斜杠也按字面处理, 不支持注释、转义或 NAME=value 语法。
  • 拒绝空文件、空行、NUL、中间的 CR/LF、单独的末尾 CR 和多余空行。只含空格的口令不算空。
  • 必须是可读普通文件,路径中不能经过符号链接。FIFO、目录不受支持,- 不代表标准输入。 macOS 的系统别名 /tmp、/var、/etc 会规范化为对应的 /private 路径。

建议放在仓库与公开服务目录之外,文件权限设为 0600,父目录仅允许所需用户访问。 SOW 不强制要求权限恰好为 0600,因此访问控制适当的只读 secret 挂载也可使用。 能读取文件的用户或进程仍然能取得口令。

SOW 只在签名或签名前置检查时读取所选来源。文件不存在、不可读或格式错误时直接失败, 不会尝试其他来源,也不会退回密码输入框。指定来源后,外部 GPG 使用 batch loopback 模式, 需要 agent 允许该模式;要求交互确认的硬件密钥仍须满足其自身要求。 不新增直接传递明文口令的参数或环境变量。

支持该选项的命令包括 create(同时指定 --sign-with)、init、config check、 repo new/rm/migrate、dist new/rm、add、rm、build 和 export rpm-leaf。 add --skip 仍可能签包,rm --check 可能渲染带签名的预览元数据。 publish、本地与目标端 gc、log prune 也支持,因为恢复中断的变更可能需要签名。 查询命令、config show 和只验证签名的 check 不读取口令文件。 retain 与 Plain prune 不接受 --passfile。 恢复时从本次执行和当前配置重新选择来源,口令值不会写入恢复日志。

只修改 passfile 路径不会改变签名身份,也不会让 Dist 变脏。 口令值不会进入日志、配置输出、仓库状态或公开文件。 外部元数据签名通过独立文件描述符传入口令;RPM 可能多次调用 GPG,因此签包时会在 私有的 /tmp/sow-gpg-pass-* 目录内创建 0600 临时文件,调用返回后删除。 进程被强制终止时,临时文件可能残留。

Passphrase 引用

元数据签名中的旧 passphrase 字段继续兼容路径、file:// 和 env:// 引用, 现在也可配合 GPG 密钥标识使用。它优先于 SOW_GPG_PASSFILE,可被 --passfile 覆盖。 同一密钥的 passfile 与 passphrase 都非空时会报配置错误。新配置建议使用 passfile。 从 passphrase 改为 passfile 可能需要重建一次元数据,因为旧配置身份包含旧引用。

旧引用保持原来的字节处理方式:最多 64 KiB、非空、不含 NUL,先去掉末尾一个 LF, 再去掉末尾一个 CR。进程内签名继续兼容旧口令中的其他字节;外部 GPG 要求单行。 旧文件引用中的相对路径以工作区根为基准,并沿用密钥文件的普通文件和无符号链接规则。

对 config check,引用语法错误返回 2,签名引用无法解析或不可用时返回 6。 元数据私钥文件会被解锁并检查是否适合签名;外部 GPG 元数据密钥还会执行一次试签。 RPM 包签名只检查所选口令文件的格式、工具与私钥是否可用,不会签包,也不能证明 口令正确;实际签名和验签发生在包需要签名时。

不带 --all 时,config show 也可能显示 key_fingerprint。 启用的 RPM 包签名设置还可能显示 key_snapshot_sha256、trusted_key_fingerprints 和 trusted_key_snapshot_sha256s。指纹与快照数组各自排序,不能按下标配对。 这些是公钥身份的输出字段,不是 sow.yml 接受的配置键。

密钥轮换和包复用见签名。

发布目标

targets:
  mirror:
    repository: demo
    provider: filesystem
    endpoint: file:///srv/mirror
    prefix: demo
    public_endpoint: file:///srv/mirror/demo/
    max_cache_ttl: 0s
    authoritative_workspace: true
    single_writer: true
    exclusive_write_authority: true
字段 是否必填 含义
repository 是 已有仓库名称
provider 是 filesystem 或 r2
endpoint 是 无尾部斜杠的规范 file:///absolute/path,R2 使用 https://host
region 仅 R2 必须为 auto,filesystem 禁止
bucket 仅 R2 小写、符合 DNS 标签规则的桶名,filesystem 禁止
prefix 否 相对存储前缀,空值使用存储命名空间根
credential 仅 R2 env://NAME 或 file:///absolute/path,filesystem 禁止
public_endpoint 是 以 / 结尾的规范验证 URL;filesystem 接受 HTTP(S) 或 file://,R2 接受 HTTP(S)
max_cache_ttl 是 规范的非负 Go duration,零值也要显式写 0s
authoritative_workspace 是 必须为 true
single_writer 是 必须为 true
exclusive_write_authority 是 必须为 true

最后三个字段声明此工作区负责发布,并且是目标命名空间的唯一写者。 同一存储上的目标前缀不能重叠,filesystem 目标也不能指向重叠目录。

使用规范时长,如 0s、30m0s、24h0m0s;24h 会被拒绝。 时长上限低于 Go 最大 duration,为缓存宽限期预留空间。 该值应覆盖公开服务使用的最长缓存寿命。

发布前先创建 filesystem endpoint 目录。它必须是真实的规范目录,不能是符号链接。 SOW 在其下创建 prefix。

R2 目标增加存储与凭据字段:

targets:
  prod:
    repository: demo
    provider: r2
    endpoint: https://0123456789abcdef.r2.cloudflarestorage.com
    region: auto
    bucket: packages
    prefix: demo
    credential: env://SOW_R2_CREDENTIAL
    public_endpoint: https://repo.example.com/demo/
    max_cache_ttl: 24h0m0s
    authoritative_workspace: true
    single_writer: true
    exclusive_write_authority: true

被引用的环境变量或文件应包含一个 JSON 文档:

{"access_key_id":"YOUR_ACCESS_KEY_ID","secret_access_key":"YOUR_SECRET_ACCESS_KEY"}

还可包含 session_token。未知字段、多余尾部数据、缺失凭据或超过 64 KiB 的内容都会被拒绝。 凭据文件必须解析为普通文件,允许指向普通文件的符号链接。 凭据不会写入公开目录或配置输出。

首次发布绑定仓库、Provider、存储身份和前缀。修改目标名称、public_endpoint 或 max_cache_ttl 时,使用 publish --rebind。 Provider、endpoint、region、bucket、prefix 和仓库身份不能重新绑定,应配置新目标。 待完成的维护操作可能阻止修改缓存 TTL 或公开 endpoint,需先完成维护。

固定设置

仓库路径固定为 <workspace>/<name>,APT component 固定为 main。 架构视图由 Dist 配置与包内信息派生。构建快照保留使用 retain 管理,不在配置中设置保留数量。

继续阅读布局、发布和 退出码。

3 - 包引用

按摘要、坐标、文件名或包名选择软件包,并解决匹配歧义。

rm、show 和 where 接受包引用,add 接受文件或目录路径。 先用 sow ls 查到包坐标或 SHA-256,再传给其他命令。

sow ls -r demo -d el9
sow show 'rpm:example-0:1.0-1.x86_64' -r demo

支持的形式

形式 语法 匹配对象
SHA-256 sha256:<64 位小写十六进制数字> 已存储的包字节
RPM 坐标 rpm:<name>-<epoch>:<version>-<release>.<arch> 包含 epoch 的完整 RPM 身份
DEB 坐标 deb:<name>=<version>:<arch> 完整 DEB 身份
文件名 example-1.0-1.x86_64.rpm 已存储的包文件名
裸包名 example 二进制包名

SHA-256 必须完整且小写。SOW 拒绝缩写或大写摘要,退出码为 6。 摘要对应已存储字节;Managed RPM 签名可能使它与输入文件摘要不同。

RPM 坐标始终包含 epoch,包没有 epoch 时用 0。SOW 按完整存储坐标匹配带 rpm: 前缀的引用。 匹配时必须提供坐标的全部字段。

DEB 坐标包含完整 Debian 版本(含 epoch、revision)与原生架构。 DEB 使用 amd64、arm64 或 all;RPM 使用 x86_64、aarch64 或 noarch。 这些值来自包本身,不是配置别名。

sow where 'rpm:example-0:1.0-1.noarch'
sow where 'deb:example=2:1.0-1:amd64'

文件名方便输入,但不一定唯一。脚本优先使用坐标或完整摘要。 包引用不支持路径、通配符、正则表达式或版本范围。

包名匹配与歧义

命令 裸包名匹配多个版本或架构时
rm 移除所选 Dist 中的全部匹配成员
show 拒绝歧义引用并列出候选
where 要求唯一包身份,并返回该身份在各仓库中的位置

Plain sow prune --protect NAME 也只接受精确的二进制 RPM 包名(不接受坐标、摘要或文件名)。

按包名移除前可先预览:

sow rm example -r demo -d el9 --check

show 或 where 遇到歧义时,从候选列表选择坐标或摘要。 多个仓库保存相同格式、坐标和 SHA-256 时,where 可以返回多个仓库位置。 仓库中的版本或字节不同时,使用 -r 或更精确的引用。

其他引用形式在单个仓库中必须确定一个对象。匹配不到或存在歧义时,退出码为 6。

搜索范围

rm 和 where 搜索期望成员。不带 -d 时,show 搜索全部已记录包对象, 包括已退出 Dist 的包;带 -d 时,只搜索所选 Dist 的期望成员。未记录的文件不参与查询。

命令 默认范围 选择方式
rm 一个仓库中所选 Dist 的成员 -r;可重复的 -d,或推断出的 Dist
show 所选仓库的全部已记录包对象 -r;-d 限定为所选 Dist 的期望成员
where 工作区全部仓库 可选 -r、-d

rm 无法推断 Dist 且仓库中有多个 Dist 时,需要指定 -d。 不带 -r 的 where -d NAME 搜索包含该 Dist 名称的仓库。 所请求的 Dist 名称在所有仓库中都不存在时,命令报错。

坐标冲突

一个仓库内,一个坐标最多对应一个内容对象。已有坐标下加入不同包时,SOW 拒绝覆盖。 仅改变签名也会改变完整字节身份。

启用 RPM 包签名时,坐标、排除签名头后的包体摘要和签名策略匹配,就可以复用已有签名对象。 其他情况下,改变字节应使用新的发行版本和文件名。详见签名。

命令选项见 rm、show和 where。

4 - 仓库布局

查找包文件、仓库索引、私有状态和发布输出的位置。

对外服务完整的 Managed 仓库目录:pool/ 加 dists/。 工作区配置与 .sow/ 状态应位于 Web 服务根目录之外。

Plain 目录

  • /srv/offline/
    • example-1.0-1.x86_64.rpm
    • example_1.0-1_amd64.deb
    • repodata/
      • <sha256>-primary.xml.gz
      • <sha256>-filelists.xml.gz
      • <sha256>-other.xml.gz
      • repomd.xml
    • Packages
    • Packages.gz
    • repo_complete使用 --pigsty 时

sow create 在存在 RPM 时生成 RPM 元数据,存在 DEB 时生成 DEB 元数据。sow prune 作用于平面 RPM 目录,按包名和架构保留 EVR 最高的版本。 RPM 位置使用文件名,DEB 使用 ./<文件名>。构建期间使用私有 .sow-plain-stage-* 目录,活动 prune 通过 .sow-prune-repo-complete 跟踪完成标记; 对外服务和复制时应排除这些私有路径。后续 create 会清理过时的 SOW 临时路径并重建索引。

.sow-plain-rpm-history.json 记录当前与上一轮 RPM 元数据引用。 复制目录后还要执行 create 或 prune 时,一并保留它。它不属于客户端索引,也不是恢复日志。

Managed 工作区

  • <workspace>/
    • sow.yml工作区配置
    • .sow/私有状态
      • workspace.lock
      • workspace-ops/
      • repo-locks/<repo>.lock
      • <repo>.db
      • <repo>/
        • abandoned/中止迁移时保留的暂存文件
        • stage/
        • recovery/
        • retained/构建快照保留记录与 .rm 删除计划
        • transitions/
        • pending/
        • gc-plan.json活动的本地 GC 删除计划
    • <repo>/公开仓库根
      • pool/
      • dists/

数据库旁可能有 SQLite WAL 和共享内存文件。.sow/ 与 pending 目录权限为私有的 0700。 待构建包文件使用 0644,进入公开路径时无需改变权限。

SOW 0.7.1 使用数据库 schema v13 和配置 schema sow/v3,两者是独立的版本标识。 执行 repo migrate 前备份完整工作区。 数据库应与匹配的私有、公开状态一起保存;不要手动修改 schema 版本或可重建缓存。

包池

pool/<prefix>/<source>/<filename>

source 来自 RPM SOURCERPM 或 DEB Source 字段,规范化为源码包名。 字段缺失时使用二进制包名。前缀为首字符的小写形式;源码名以小写 lib 开头时, 取前四个字符并转为小写,不足四个字符则取全名。

源码名 示例路径
postgresql-18 pool/p/postgresql-18/libpq5_18.3-1_amd64.deb
example pool/e/example/example-1.0-1.x86_64.rpm
libfoo pool/libf/libfoo/libfoo1_1.0-1_amd64.deb

包对象不可变。移除成员关系后,字节仍可被构建、保留、恢复和发布记录使用。 这些记录都不再需要某个对象后,gc 才会删除它。 每个仓库有独立包池。

RPM Dist

  • dists/el9/
    • x86_64/repodata/
      • <sha256>-primary.xml.gz
      • <sha256>-filelists.xml.gz
      • <sha256>-other.xml.gz
      • repomd.xml
      • repomd.xml.asc配置元数据签名时
    • aarch64/repodata/

架构视图只包含元数据,包位置指向根包池:

<location href="../../../pool/e/example/example-1.0-1.x86_64.rpm"/>

原生架构包只出现在对应视图,noarch 包出现在全部配置视图。 旧的校验和命名元数据可能与当前文件共存,repomd.xml 选择当前生效的一组。

需要包路径都在目录内的独立副本时,使用 export rpm-leaf。

DEB Dist

  • dists/trixie/
    • Release
    • InRelease配置元数据签名时
    • Release.gpg配置元数据签名时
    • main/
      • binary-amd64/
        • Packages
        • Packages.gz
        • by-hash/SHA256/<digest>
      • binary-arm64/

Packages 使用相对仓库根的路径,如 Filename: pool/e/example/example_1.0-1_amd64.deb。 Release 包含 SHA-256 清单与 Acquire-By-Hash: yes。 架构为 all 的包进入每个适用索引。

发布路径

两种发布 Provider 都接收相同公开布局:

  • <prefix>/
    • pool/
    • dists/

导出拥有自己的包文件、元数据、清单和 .sow-export.json 完成标记。 它是独立交付制品,不是另一个 Managed 仓库。

名称与大小写

仓库、Dist 和目标名称使用 [a-z0-9][a-z0-9._-]*。 保留名称包括 .、..、.sow、pool、dists、sow.yml、workspace.lock、 workspace-ops、repo-locks。

SOW 拒绝与已知路径仅大小写不同的新包池路径,即使字节相同。 在大小写不敏感文件系统上,仅大小写不同的源码目录也会冲突; 在大小写敏感文件系统上,这些源码目录可以共存,因此迁移到大小写不敏感卷前应先检查。

继续阅读视图、对外服务和 平台。

5 - 退出码

退出码 0–6 与中断码 130 的含义和示例。

脚本使用退出码判断结果,具体原因读取错误诊断。SOW 各命令使用相同的退出码含义。

退出码 含义 下一步
0 成功或无需变更 查看结果;无需变更时可能报告 noop: true
1 运行时 I/O、包解析、渲染或其他未分类错误 修正诊断指出的输入或环境问题后重试
2 命令、工作区查找或配置错误 检查 sow help COMMAND、选择参数和 sow config check
3 部分成功 查看结果,区分已提交工作与失败项
4 无法取得写锁 稍后重试或增加 --timeout
5 完整性/恢复错误,或仓库尚不可交付 查看 status、check;构建脏仓库或恢复已记录操作
6 操作被拒绝 修正错误指出的冲突、包引用、架构、签名或保护设置
130 命令收到 SIGINT 或 SIGTERM 而中断 按中断操作的类型选择恢复命令

成功不一定表示可以交付

只要能够报告状态,status 就返回 0,包括 dirty、recovering、error。 其 ready_to_copy 字段是状态摘要,不是完整字节检查。交付前使用 check:

sow build -r demo
sow check -r demo

脏仓库的旧索引可以完全一致,但由于期望状态尚未构建,check 仍返回 5。 运行 build 后再检查。如果诊断指出包损坏或恢复证据冲突,先查明该错误; 具体处理方法见故障处理。

部分成功

add 返回 3,表示 SOW 接受或复用了至少一个输入,同时至少一个输入失败。 JSON 结果保留每个输入的处理结果。策略排除不算输入失败。 init 在创建部分已声明对象、后续对象失败时,也可能返回 3。 prune 在发布筛选后的索引或删除至少一个 RPM 后发生错误时,同样返回 3。

先保存输出与退出码,再决定是否继续:

#!/usr/bin/env bash
set -uo pipefail

sow add ./incoming/ -r demo -d el9 --json > add-result.json
rc=$?
case "$rc" in
  0) ;;
  3) jq '.result.items[] | select(.status == "failed")' add-result.json
     exit 3 ;;
  4) echo 'Repository is busy; retry later.' >&2
     exit 4 ;;
  *) exit "$rc" ;;
esac

sow check -r demo || exit $?
sow publish mirror

mirror 必须是 demo 的已配置发布目标。此脚本在部分成功时停止, 以便先检查失败输入,再决定发布。

等待锁与中断

默认 -T 0 一直等待写锁,-T 30s 最多等待 30 秒,-N 只尝试一次。 占锁时返回 4;-N 与非零 -T 同用返回 2。

只读命令不接受这些选项。部分读命令使用共享锁获取一致快照; status 无需等待仓库写者即可报告写锁状态。

SOW 处理 Ctrl-C 或 SIGTERM 中断时返回 130。上传进度超时等内部超时保留其普通错误分类。 中断不会撤销已经提交的工作。先检查状态,再按事务与恢复 中对应的流程继续操作。

JSON 错误

使用 --json 时,stdout 输出结果封装,stderr 输出诊断。 errors 数组使用下列分类:

退出码 JSON class
1 runtime
2 usage、discovery 或 config
3 partial
4 lock
5 integrity
6 rejected
130 interrupted

按退出码或分类判断,不解析错误消息文本。Plain create 与 prune 向 stderr 打印其内部错误, JSON 消息可能额外包含 CLI 分类前缀。即使 ok 为 false,诊断和部分成功结果也会保留。

详见 JSON 输出与 事务与恢复。

6 - JSON 输出

查阅 JSON 响应字段、包与操作标识、错误和各命令结果。

数据命令使用 --json 后,会向 stdout 输出一个以换行结束的 JSON 文档,诊断写入 stderr。 log export 例外:它输出 JSON Lines,不接受 --json。

sow status -r demo --json | jq '.result.status'

顶层封装

每个 JSON 响应都有七个字段:

字段 类型 含义
schema 字符串 sow.cli/v1
command 字符串 包含子命令的名称,如 repo ls
ok 布尔值 命令成功;存在错误时为 false
repository 字符串或 null 响应关联的所选仓库
operation 字符串或 null 命令对应的操作或发布身份
result 对象或 null 命令数据;失败时也可包含有用的部分结果或诊断
errors 数组 包含 code、class、message 的错误对象

配置检查成功的示例:

{
  "schema": "sow.cli/v1",
  "command": "config check",
  "ok": true,
  "repository": null,
  "operation": null,
  "result": {"workspace": "/srv/repo", "repositories": 1, "dists": 2},
  "errors": []
}

实际输出只有一行,路径和计数随工作区变化。没有产生有意义结果就失败时,通常为 result: null。失败的 check 和部分成功的 add 仍可返回有用结果; ok 为 false 时也应保留它们。

数据库提交后的维护错误也可能返回已提交结果。 重试前查看结果、status 和操作日志。非零退出码不代表事务已经回滚。

错误

{"code":3,"class":"partial","message":"managed: batch partially succeeded"}

按 code 或 class 判断,不解析消息文本。分类有 runtime、usage、discovery、 config、partial、lock、integrity、rejected、interrupted。 分类对应关系和下一步见退出码。

身份标识

操作 ID 与构建快照 ID 保持字符串类型。操作 ID 可能超出 JavaScript 精确整数范围。 构建快照 ID 是补零至 20 位的十进制字符串:

{"operation":"8632724976452398569","built_generation":"00000000000000000004"}

此规则适用于 generation、built_generation、base_generation 和表示构建快照的 base。 期望状态修订号和计数仍是 JSON 数字。

SOW 按下列规则设置顶层 operation:

命令 值
add、非预览 rm、build、本地 gc 已分配的日志 ID
publish、publish --abort 已有的发布尝试 ID
log ID 请求的操作 ID
其他命令(create、prune、init、config、ls、show、where、retain、export) null;log prune 将 ID 放在 result.operation

operation 为 null 不表示只读命令。create、prune、init、config check、repo ls 和 where 的顶层 repository 为 null;where 将具体仓库名称放在位置记录中。

Plain create 与 prune

sow create 在 result 中返回下列字段:

结果字段 含义
dir 已索引目录的绝对路径
rpm、deb 进入索引的包数量
kept 排序后的保留包文件名
removed --pigsty 删除的文件
marker 是否生成完成标记
marker_sha256 标记摘要,生成标记时存在
noop 无需改变公开文件
recovered 始终 false,Plain 没有日志恢复
signed、signer 已签名文件名和可用的签名者身份
dropped_metadata create 替换已有 repomd.xml 时移除的不受支持数据类型(如 group、updateinfo、modules),存在时返回

sow prune 在 result 中返回下列字段:

结果字段 含义
dir 目标目录路径
dry_run 是否设置了 --dry-run
kept 排序后的顶层保留 RPM 文件名
candidates {path, latest} 对象数组,列出每个待删除候选包及其保留的替换 RPM
removed 已删除的 RPM 文件名
index_updated 是否重写了 repodata/
marker 操作完成后是否存在 repo_complete
noop 无需删除软件包且未写入索引时为 true

初始化与配置

命令 结果
init workspace、config_created、repositories_initialized、dists_initialized、existing
config check workspace、repositories、dists
config show 生效配置:schema、architectures、repos,以及存在时的 targets

解析后的签名设置可包含公钥指纹和证书快照摘要。指纹和摘要数组各自排序,不能按下标配对。 详见配置参考。

仓库与 Dist

repo ls 返回 result.repositories[];repo new 与 repo show 直接返回一个仓库对象。 常见字段为 name、path、protected、dists、generation、desired_revision、 status、packages、memberships、config。 有相应信息时还会出现 dirty_reasons、recent_operation。操作摘要包含 id、kind、state、created_at、updated_at 和可选 error_class。 status.pending_operation、status.recent_operation 也使用这一结构。

packages 统计已存储包对象,memberships 统计期望 Dist 成员关系。 一个对象属于两个 Dist 时,分别计为一个包、两份成员关系。

dist ls 返回 result.dists[];dist new 与 dist show 直接返回一个 Dist 对象: name、format、architectures、desired_members、built_members、generation、 dirty、status、effective_config_sha256、config,以及可选 dirty_reasons。

每个架构条目有 family 和 ecosystem_arch。例如 DEB 的规范族为 x86_64, 生态名称为 amd64。

repo rm 和 dist rm 返回 name、removed、noop。 repo migrate 返回 repository、repository_id、from_layout、to_layout、 schema_from、schema_to、phase、generation、grace_not_before、legacy_aliases、 complete,以及可选的 warnings。没有宽限期截止时间时,grace_not_before 为 null。 迁移中止告警会指出保留的暂存目录或未完成的清理。 仓库选择成功后,即使迁移失败,响应中也会保留所选仓库。 各阶段含义见 repo migrate。

add、rm 与 build

三者都报告操作后的仓库状态:repository、desired_revision、built_generation、dirty。

add 还返回 operation、accepted、failed、memberships_added、memberships_removed 和 items[]。每项包含 input、status;有相应信息时,还包含 format、coordinate、 sha256、各 Dist 的 dists 结果映射及 error。

输入状态 含义
accepted 新包进入成员集合
reused 复用已有包对象,成员关系仍可能改变
excluded 策略导致所有选中 Dist 都未接受该包
failed 输入失败,查看 error

每个 Dist 的结果为 accepted、excluded 或 limited。一个包在不同 Dist 中可以有不同 结果。limit 移除旧版本时,成员移除计数会增加。

rm 返回可选的 operation、removed[]、所选 dists、changes[] 和 check。 每条 removed 记录包含 dist、sha256、coordinate、name。 预览时 check: true;只有 prediction_complete: true 才表示预期文件列表完整。 rm 不删除包池字节。

build 返回 operation、考虑的 dists 和 noop。无需变更时不创建新构建快照。

status 与 check

status 返回 status、ready_to_copy、desired_revision、built_generation、 dirty_dists、dirty_reasons、pending、repository_locked。 可选字段有 pending_operation、recent_operation、lock_holder。

status 为 clean、dirty、recovering 或 error。pending 包含 count、bytes。 成功的状态查询可以返回任何仓库状态。ok: true 表示查询成功。发布或复制前,用 check 做完整校验。

check 返回 repository、status、ready_to_copy、built_generation、 desired_revision 和 layers[]。每层有 name、ok、checked、issues。

正常检查层依次为 config、state、public-modes、retained、package-bytes、 desired-membership、index、signature、generation-manifest。 存在活动布局转换时,改为前三层加 layout-transition。 脏仓库可以通过各项一致性检查,但仍因尚不可交付而返回 5。

changes

结果包含 repository、base、generation、dirty、changes[],每项变更包含:

字段 取值
op add、update、delete
phase payload、metadata、pointer、delete
path 相对仓库根的路径,使用 / 分隔
size、sha256 新增或更新文件有此字段,删除项省略

不传基线时与上一构建快照比较;基线 0 列出当前完整目录;数字基线与指定的已记录构建快照比较。 这是本地文件差异,远端变更使用 publish 和目标 gc。

包查询

命令 结果字段
ls repository、所选 dists、dirty、packages[]
show repository、package
where reference、locations[]

包对象包含 sha256、format、coordinate、architecture、canonical_arch、pool_path、 filename、size、name、source、version、kind、storage、created_revision、dists、 built_dists。可选字段包括 epoch、release、RPM 的 payload_sha256、signature_key,以及 warning。

architecture 是原生名称(如 noarch、amd64),canonical_arch 为 x86_64、aarch64 或 neutral。storage 为 pool 或 pending,不表示远端发布状态。 dists 是期望成员,built_dists 是上次构建成员。 包对象的 coordinate 不含格式前缀,加上 format + ":" 后才是 CLI 坐标引用。 where 位置记录的坐标已含此前缀。每个位置包含 repository、dists、 built_dists、sha256 和 coordinate。

发布与维护

命令 主要结果字段
publish repository、target、provider、generation、可选 attempt/checkpoint、phase、objects、noop
publish --abort repository、target、provider、attempt、phase、objects
retain add/rm repository、record、record_identity、path
retain ls repository、generations[]
本地 gc 可选 operation、repository、base_generation、generation、objects、bytes、noop
目标 gc repository、target、provider、phase、reports、candidates、deleted_objects、deleted_bytes、retained_objects、pending_grace、completed_attempts、noop
export rpm-leaf repository、repository_id、generation、dist、arch、directory、method、signed、signer_identity、packages、files、manifest_sha256

publish --rebind 使用普通 publish 结果结构。R2 目标 GC 只报告候选,不删除远端对象。

操作日志

log 返回 repository 和按新到旧排序的 operations[]。 log ID 返回 repository 与 detail,后者包含 operation、duration_ms、events、 packages、memberships 和 files。

详情数组 记录字段
events sequence、state、detail_json、occurred_at
packages sequence、input_path、disposition;可选 package_sha256、coordinate、error_class、message、dists
memberships sequence、dist、package_sha256、action
files sequence、action、phase、path;可选 size、sha256

payload_json、result_json 和事件的 detail_json 是包含 JSON 的字符串, 新的元数据构建操作在 payload_json 中包含 publication_time;旧日志与布局迁移可能没有该字段。 需要时单独解析这些 JSON 字符串:

sow log -r demo --json \
  | jq '.result.operations[] | .payload_json | fromjson'

log prune 返回 operation、repository、before、pruned、compaction_deferred。 仅日期参数按机器本地时区解析,before 包含解析后的时间戳。

log export 每行输出一个完整操作记录,没有顶层封装:

sow log export operations.jsonl -r demo
sow log export - -r demo

文件目标必须尚不存在,其直接父目录必须是真实目录,不能是符号链接。

在脚本中使用检查结果

#!/usr/bin/env bash
set -euo pipefail
sow check -r demo --json | jq -e '.ok' >/dev/null
sow changes 0 -r demo --json \
  | jq -r '.result.changes[] | [.phase, .op, .path] | @tsv'

此脚本检查本地仓库并输出当前文件清单。需要发布时,对已配置目标运行 sow publish TARGET。

7 - 平台与集成

支持的平台、文件系统要求、仓库格式、客户端与集成测试覆盖。

SOW 在 Linux 和 macOS 上构建 RPM、DEB 仓库文件。工作区使用受支持的本地文件系统, 再为客户端配置 HTTP 或存储访问。

平台

操作系统 amd64 arm64 发布格式
Linux 是 是 归档、RPM、DEB
macOS 是 是 归档
Windows 否 否 —

发布二进制使用 CGO_ENABLED=0,不需要语言运行时。源码构建要求 Go 1.27.4 或更新版本。 归档包含 README、更新日志、Apache-2.0 许可证与第三方许可声明; Linux 软件包随二进制包含许可文件。用 sow version 查看版本、平台和构建工具链。

工作区文件系统

使用支持建议锁、fsync 和同文件系统原子重命名的本地 POSIX 文件系统。 NFS 等网络文件系统不是受支持的工作区位置。 macOS 上使用 APFS;所需原子目录交换不可用时,SOW 会拒绝构建。

工作区公开目录和私有暂存区放在同一文件系统。 只对外服务 <workspace>/<repo>/。你可以把完整的 pool/ + dists/ 复制到其他存储, 无需附带私有数据库或日志。

SOW 拒绝仅大小写不同的包池完整路径。在大小写不敏感文件系统上,SOW 还会拒绝源码目录的大小写别名。 大小写敏感卷上创建的仓库可能包含目标卷无法区分的源码目录,filesystem 发布会在开始前检查。 详见布局。

仓库格式与客户端

输出 客户端入口 包位置
Plain RPM repodata/repomd.xml 与 repodata/ 同级
Plain DEB Packages 或 Packages.gz 与索引同级,使用平面 APT 源
Managed RPM dists/<dist>/<arch>/repodata/repomd.xml 通过相对路径回到根 pool/
Managed DEB dists/<dist>/Release 或已签名的 InRelease 相对于仓库根的 pool/ 路径

Managed APT 使用 component main 和 SHA-256 by-hash 索引。 元数据签名生成 RPM repomd.xml.asc 或 APT InRelease、Release.gpg。

发布时间

新的 Managed 操作在构建元数据时,会记录本次操作的发布时间;sow repo migrate 保持原有时间戳行为。 APT Release 日期、RPM repomd.xml 的 data timestamp 与元数据签名使用同一时间。 RPM revision 仍为构建快照(Generation)编号。

恢复操作复用已记录的时间。没有记录发布时间的旧日志保持原有时间戳行为。 没有实际变更的操作不会刷新发布时间。

RPM 的 XML 与 SQLite 元数据

SOW 生成 gzip 压缩的 primary、filelists、other XML,不生成 SQLite 格式的 primary_db、filelists_db、other_db。两者是不同元数据格式; 只读取 primary_db 的工具需要增加 XML 输入支持或单独转换。

缺少 primary_db 本身不会阻止 YUM 读取仓库。上游 YUM 在没有可用 SQLite 索引时, 会读取 XML 并生成本地缓存,见 YUM 元数据加载实现。 SOW 输出的 XML 使用 gzip,因此比较其他仓库或工具时,zstd 支持是另一个问题。

SOW 不生成 zchunk、modulemd 或源码包索引。客户端需要模块 profile 或 dnf module install 时,应保留提供模块元数据的工具。 将模块化包作为普通 RPM 安装时,按需配置客户端 module_hotfixes,详见 迁移指南。

dnf reposync

默认 dnf reposync 将下载限制在单仓库目录,因此会拒绝规范 Managed RPM 视图中指向父目录 的包路径。这符合其安全写入路径规则。 该工作流使用 sow export rpm-leaf 生成自包含目录。

已有 EL7 缓存

EL7 YUM 会把元数据 data timestamp 与已缓存仓库比较。 Plain create --metadata-timestamp SECONDS 和 prune --metadata-timestamp SECONDS 可以保留并推进已有发布时间,默认值为 0。

导出的 RPM 仓库,以及 sow repo migrate 本次生成的 RPM 元数据,使用时间戳 0,没有覆盖选项。 之后的 Managed 构建使用其记录的发布时间。 对这些输出,如果现有客户端仓库 ID 已缓存正数时间戳,使用新仓库 ID,或清理该 ID 的元数据缓存。 只改变 base URL 不够。需要保留旧 ID 和缓存时,使用 Plain 模式,按 YUM 迁移 的时间戳流程操作。

RPM 签名

包签名工作流支持 RPM 格式 v4。SOW 验签支持 RPM 签名头标签 267(RPMSIGTAG_DSA)中的 RSA、DSA、ECDSA 和 EdDSA(Ed25519)OpenPGP 数据包(rpm 4.16 及更新版本将 ECDSA 与 EdDSA 签名保存在该标签中)。Managed 模式与 Plain create / prune 均拒绝 RPM 格式 v6 软件包与签名标签。 元数据密钥引用是独立功能,详见签名。

发布 Provider

Provider 发布 目标 GC
filesystem 写入已有规范 file:// endpoint 之下 缓存宽限期后条件删除,再验证公开入口已不可获取
r2 通过 S3 兼容存储操作写入 只报告候选,不删除远端对象

两者都在配置前缀下发布完整仓库。存储对应的 HTTP 服务、DNS、凭据、访问策略和缓存行为 需要另行配置。public_endpoint 告诉 SOW 从哪里验证公开内容,不会创建服务。 R2 公开 endpoint 必须使用 HTTP(S),filesystem 还支持 file:// 验证。

自动化覆盖

源码包含以下集成检查:

检查 环境 范围
CLI 干净环境流程 Linux CI 交付二进制、混合 Plain 输出、Managed 初始化、RPM/DEB Dist、查询、构建与检查
Plain APT 客户端 pgsty/u22 Ubuntu 22.04 容器 显式信任未签名源时的 HTTP 更新、包发现、版本选择、下载与安装
RPM 分离签名切换 AlmaLinux 8/9 与 pgsty/el10 容器 repomd.xml 及分离签名切换时的 DNF 行为,以及带口令保护 GPG 密钥的口令文件签名
S3 兼容传输 固定版本的 PGSTY Silo 容器 Bucket 列表、HEAD、GET、条件式单段 PUT、重放和对象 SHA-256 元数据
发布打包 Linux CI 四个归档、两个 RPM、两个 DEB、校验和、包内路径与许可文件

DNF 测试检查签名切换,APT 测试使用未签名 Plain 输出。 完整的已签名 Managed 安装及托管 R2 条件式分段上传完成,需要在部署环境检查。

切换客户端前,运行 sow check,发布到目标,再使用实际客户端配置刷新索引并安装一个包。 客户端设置见对外服务。