这是本节的多页打印视图。 .
上手
SOW 生成 RPM/YUM 与 DEB/APT 静态仓库,本身不是 HTTP 守护进程。先选择一条相互隔离的 运行路径:
-
Plain:
sow create在普通目录中为现有软件包重建索引。 -
Managed: 工作区持续记录成员关系、Dist、架构视图、策略、签名、Generation、 审计历史与发布目标。
-
安装 — 选择 Release 归档、RPM/DEB 安装包或源码构建,并校验二进制。
-
快速上手 — 从一个软件包目录创建平面仓库,并通过 HTTP 提供服务。
-
第一个工作区 — 初始化 Managed 模式,创建 RPM/DEB Dist,添加软件包,然后构建并校验。
-
核心概念 — Workspace、Repository、Dist、Package Object、Desired Membership 与 Built Generation。
Managed 工作区需要具备建议锁、fsync 与原子 rename 语义的本地 POSIX 文件系统。元数据
在进程内生成;可选的 RPM 包签名需要 rpm,agent:// 元数据密钥需要 gpg 与
gpg-agent。
1 - 安装
SOW 只有一个可执行文件,不需要启用服务,也不依赖语言运行时。Release 构建目标是 Linux 与
macOS 的 amd64、arm64;Linux 另外提供 RPM 与 DEB 安装包。不支持 Windows。
在下载页选择匹配操作系统与架构的归档或 Linux 安装包。页面同时提供每个
已发布制品、对应源码 Tag 与 SHA256SUMS 的链接。
安装归档
下载一个归档与 SHA256SUMS,解压前只校验对应条目:
macOS 选择 darwin_amd64 或 darwin_arm64,并把 sha256sum -c - 换成
shasum -a 256 -c -。没有 root 时,把二进制装到已经加入 PATH 的目录,例如
~/.local/bin。
安装 Linux 软件包
Linux 软件包使用 1PGSTY Release 后缀:
只执行符合本机发行版与架构的那条命令。RPM 把 License 安装到
/usr/share/licenses/sow/LICENSE;DEB 把版权/协议文件安装到 /usr/share/doc/sow/。
从源码构建
Go Module 声明使用 Go 1.27.0,元数据生成不需要 C 工具链。请把 vX.Y.Z 替换为下载页
链接的源码 Tag:
这组命令使用 Release 构建参数,并把所选 Tag 的产品版本写入二进制。
校验
sow version 输出产品版本、目标 OS/架构与构建 Go 工具链;sow help 列出命令树。
归档中还包含 README.md、CHANGELOG.md 与 Apache-2.0 LICENSE。
升级 0.3 Managed Workspace
SOW 0.4 引入内部数据库 Schema v11 与 v12。公共布局和 schema: sow/v3 配置标识均不改变,
但每个既有 v0.3 Repository 都必须在普通读写前显式迁移。备份前先停止 Workspace 全部写入:
对 sow.yml 中的每个 Repository 重复最后两条命令。数据库 Transition 是单向的;迁移完成后
不要再用 SOW 0.3 打开 Workspace。状态、Signer 与 Publication Evidence 的修复范围见
sow repo migrate。
权限与可选工具
执行用户需要读取输入软件包,并能写入 Plain 目标目录或 Managed 工作区。Managed 工作区 应放在本地 POSIX 文件系统上;锁、fsync、安全路径与原子 rename 都属于正确性契约。
软件包解析与元数据渲染都在进程内完成。只有两条可选路径需要主机工具:
- RPM 包签名 需要
rpm与可用的 GPG 环境; agent://元数据密钥需要gpg与gpg-agent。
2 - 快速上手
Plain 模式在一个目录内生成平面仓库。它不读取 sow.yml,不创建工作区,也不维护数据库。
准备目录
把 RPM 和/或 DEB 文件放在目录顶层。sow create 不递归扫描,也不移动或改名包文件。
如果某种格式不存在,请只复制你实际拥有的软件包。
生成元数据
混合格式输出形态如下:
目录会变成:
Plain 模式不生成 DEB Release、InRelease 或 Release.gpg。RPM 与 DEB 元数据在一次
操作中生成;任何解析或渲染错误都会阻止新索引提交。
对外服务
本地检查可以使用任意静态文件服务器:
在另一个终端检查两个协议入口:
Python 服务器只适合预览;长期服务请使用正常维护的 HTTP 服务器。
配置客户端
把 REPO_HOST 换成客户端能访问的地址。
刷新索引并安装软件包:
APT source 末尾的 ./ 表示平面仓库。[trusted=yes] 与关闭 DNF 签名检查只适用于这个
未签名的快速示例;需要真实性保证时应使用已签名 Managed 仓库。
更新仓库
增删包文件后重新执行同一条命令:
目录内容就是 Plain 模式的全部状态。包字节不变时,生成的元数据具有确定性,重复运行会报告
noop=true。
自动化场景可使用带版本的 JSON 信封:
何时使用 Managed 模式
如果目录已经恰好包含要发布的全部内容,使用 Plain。需要具名 Dist、架构视图、成员策略、 已签名元数据、Generation、审计或发布目标时,使用 Managed 工作区。
另见 sow create 与
Plain 平面仓库。
3 - 第一个工作区
Managed 模式会持久保存配置、成员关系、Generation 与审计状态。下面从空目录开始。
初始化工作区
init 创建:
不要编辑或对外服务 .sow/。init 是幂等操作:重复执行会校验并收敛已声明的 Repository
与 Dist,不会重置有效工作区。
默认架构族是 x86_64 与 aarch64。配置接受 amd64、arm64 别名,并规范化为上述族名。
创建 Repository 与两个 Dist
一个 Repository 拥有一棵公共 pool/ + dists/ 树和一份私有状态数据库。每个 Dist 只有
一种格式。dist new 会立即生成合法空视图,客户端读取空 Dist 时得到空索引而不是 404。
此时公共布局为:
添加软件包
显式选择目标 Dist:
SOW 从包本身读取身份与架构,把接受的字节存入 local/pool/,更新 Desired Membership,
并在返回前构建受影响的 Dist。输入路径只用于导入;后续构建使用 Managed 包池。
需要合并多次成员变更时,用 --skip 暂不构建,最后统一收敛:
Desired Membership 领先于 Built Generation 时,Repository 状态为 dirty,且
ready_to_copy=false。
查看与校验
status 是低成本状态读取。check 是交付门禁:它校验配置、状态、公共文件权限、保留根、
包字节、Desired Membership、索引、签名与 Generation manifest,且不写入任何内容。
只有 clean 且所有层都通过的 Repository 才返回成功。
查看规范化配置与默认值:
对外服务 Repository
公共交付单元是 /srv/sow/local,不是工作区根。把这个目录挂到稳定 URL 前缀;不要暴露
sow.yml 或 .sow/。
- DNF base URL:
https://repo.example.com/local/dists/el9/x86_64/ - APT source:
deb https://repo.example.com/local bookworm main
安全的 Nginx 与 filesystem 发布流程见对外服务。
选择规则
- Workspace:从当前目录向上查找,或从
-C DIR开始查找。 - Repository:
-r NAME、当前路径所属 Repository,或唯一已配置 Repository。 - Dist:
-d NAME,可重复;只有命令能够唯一确定范围时才可省略。
存在歧义时直接报错;SOW 不会随便挑选 Repository 或 Dist。
下一步
4 - 核心概念
Plain 还是 Managed
两条运行路径相互独立。
| Plain | Managed | |
|---|---|---|
| 入口 | sow create DIR |
init、repo、dist、add、rm、build |
| 状态 | 软件包目录 | sow.yml 加私有 SQLite/操作日志 |
| 公共布局 | 平面 RPM/DEB 索引 | Repository pool/ + dists/ |
| 格式 | RPM 与 DEB 可共存于一个目录 | 每个 Dist 一种格式 |
| 架构视图 | 无 | 有 |
| 策略与审计 | 无 | 有 |
| 元数据签名与发布目标 | 无 | 有 |
目录内容已经等于目标仓库时使用 Plain。需要由 SOW 管理成员、策略、Generation、签名、 审计或发布时使用 Managed。
Managed 层级
- Workspace 是配置与发现边界。
- Repository 是隔离、Generation、发布和公共树边界。不同 Repository 之间不去重包体。
- Dist 是单一包格式的具名成员集合。
- 架构视图 是派生输出,不是第二套成员关系。
noarchRPM 与allDEB 会进入所有适用 视图,但包池字节不重复。
一条规范包体路径
Package Object 以确切字节的 SHA-256 标识;逻辑坐标来自 RPM header 或 DEB control, 不来自文件名。
每个已接受包体在 Repository pool/ 下只有一条规范路径。RPM 架构视图只含 repodata/,
包位置通过父级相对路径回到包池;APT Packages 直接指向同一包池。
普通包管理器与镜像工具是不同契约。默认 dnf reposync 会拒绝规范 RPM 视图中的父级跳转。
需要自包含 RPM 镜像 leaf 时,使用 sow export rpm-leaf 生成独立产物。
Desired 与 Built
Managed 模式分别追踪意图与公共字节:
add 与 rm 默认构建受影响的 Dist。--skip 只记录成员变更,Repository 会保持 dirty;
随后用 sow build 把 Desired 收敛为新的 Built Generation。
sow status低成本读取状态,并报告ready_to_copy。sow check执行完整只读交付证明。dirty 或 recovering 状态不可交付。sow changes [BASE_GENERATION]描述某个已记录 Generation 到当前 Built Generation 的 物理差异。它是证据与计划输出,不能替代发布恢复或远端验证。sow publish TARGET通过配置的 Provider 发布已校验 Generation,并记录 target 级恢复与 checkpoint 状态。
事务与失败状态
写操作由 Workspace 或 Repository 锁串行化,并在公共变更前记录意图。包体与不可变元数据 先准备,可变协议指针最后更新。下一条 writer 会先恢复被中断操作,再开始新工作。
| 状态 | 含义 |
|---|---|
clean |
Desired 与 Built 一致 |
dirty |
Desired 已变化;Built 仍是上一个已提交 Generation |
recovering |
存在必须解决的非终态操作 |
error |
持久证据冲突;SOW 拒绝猜测 |
用 status 诊断,用 check 做发布门禁。ready_to_copy=false 的 Repository 不应发布。