跳转到主要内容

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

返回本页常规视图.

上手

安装 SOW、创建平面仓库,并理解 Managed 工作区模型。

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 包签名需要 rpmagent:// 元数据密钥需要 gpggpg-agent

1 - 安装

通过归档、RPM/DEB 安装包或源码安装 SOW,并核对二进制与文件系统要求。

SOW 只有一个可执行文件,不需要启用服务,也不依赖语言运行时。Release 构建目标是 Linux 与 macOS 的 amd64arm64;Linux 另外提供 RPM 与 DEB 安装包。不支持 Windows。

下载页选择匹配操作系统与架构的归档或 Linux 安装包。页面同时提供每个 已发布制品、对应源码 Tag 与 SHA256SUMS 的链接。

安装归档

下载一个归档与 SHA256SUMS,解压前只校验对应条目:

# Linux amd64
grep 'sow_.*_linux_amd64.tar.gz$' SHA256SUMS | sha256sum -c -
tar -xzf sow_*_linux_amd64.tar.gz
sudo install -m 0755 sow /usr/local/bin/sow

macOS 选择 darwin_amd64darwin_arm64,并把 sha256sum -c - 换成 shasum -a 256 -c -。没有 root 时,把二进制装到已经加入 PATH 的目录,例如 ~/.local/bin

安装 Linux 软件包

Linux 软件包使用 1PGSTY Release 后缀:

sudo rpm -Uvh ./sow-*-1PGSTY.x86_64.rpm
sudo apt install ./sow_*-1PGSTY_amd64.deb

只执行符合本机发行版与架构的那条命令。RPM 把 License 安装到 /usr/share/licenses/sow/LICENSE;DEB 把版权/协议文件安装到 /usr/share/doc/sow/

从源码构建

Go Module 声明使用 Go 1.27.0,元数据生成不需要 C 工具链。请把 vX.Y.Z 替换为下载页 链接的源码 Tag:

git clone https://github.com/pgsty/sow.git
cd sow
set -euo pipefail
SOW_TAG=vX.Y.Z
git checkout "$SOW_TAG"
SOW_VERSION="${SOW_TAG#v}"
CGO_ENABLED=0 go build -trimpath \
  -ldflags="-s -w -X github.com/pgsty/sow/internal/v2cli.Version=${SOW_VERSION}" \
  -o sow ./cmd/sow
sudo install -m 0755 sow /usr/local/bin/sow

这组命令使用 Release 构建参数,并把所选 Tag 的产品版本写入二进制。

校验

sow version
sow help

sow version 输出产品版本、目标 OS/架构与构建 Go 工具链;sow help 列出命令树。 归档中还包含 README.mdCHANGELOG.md 与 Apache-2.0 LICENSE

升级 0.3 Managed Workspace

SOW 0.4 引入内部数据库 Schema v11 与 v12。公共布局和 schema: sow/v3 配置标识均不改变, 但每个既有 v0.3 Repository 都必须在普通读写前显式迁移。备份前先停止 Workspace 全部写入:

cp -a /srv/sow /srv/sow.backup-before-0.4.0
sow repo migrate REPOSITORY -C /srv/sow
sow check -r REPOSITORY -C /srv/sow

sow.yml 中的每个 Repository 重复最后两条命令。数据库 Transition 是单向的;迁移完成后 不要再用 SOW 0.3 打开 Workspace。状态、Signer 与 Publication Evidence 的修复范围见 sow repo migrate

权限与可选工具

执行用户需要读取输入软件包,并能写入 Plain 目标目录或 Managed 工作区。Managed 工作区 应放在本地 POSIX 文件系统上;锁、fsync、安全路径与原子 rename 都属于正确性契约。

软件包解析与元数据渲染都在进程内完成。只有两条可选路径需要主机工具:

  • RPM 包签名 需要 rpm 与可用的 GPG 环境;
  • agent:// 元数据密钥需要 gpggpg-agent

接下来可用快速上手进入 Plain 模式,或用 第一个工作区进入 Managed 模式。

2 - 快速上手

索引一个 RPM/DEB 软件包目录,对外服务,并配置客户端。

Plain 模式在一个目录内生成平面仓库。它不读取 sow.yml,不创建工作区,也不维护数据库。

准备目录

把 RPM 和/或 DEB 文件放在目录顶层。sow create 不递归扫描,也不移动或改名包文件。

mkdir -p /srv/repo
cp /path/to/packages/*.rpm /path/to/packages/*.deb /srv/repo/

如果某种格式不存在,请只复制你实际拥有的软件包。

生成元数据

sow create /srv/repo

混合格式输出形态如下:

created /srv/repo: rpm=1 deb=1 signed=0 removed=0 marker=false noop=false recovered=false

目录会变成:

/srv/repo/
├── package.rpm
├── package.deb
├── repodata/       # RPM: repomd.xml、primary、filelists、other
├── Packages        # DEB 平面索引
└── Packages.gz

Plain 模式不生成 DEB ReleaseInReleaseRelease.gpg。RPM 与 DEB 元数据在一次 操作中生成;任何解析或渲染错误都会阻止新索引提交。

对外服务

本地检查可以使用任意静态文件服务器:

cd /srv/repo
python3 -m http.server --bind 127.0.0.1 8080

在另一个终端检查两个协议入口:

curl --fail http://127.0.0.1:8080/repodata/repomd.xml >/dev/null
curl --fail http://127.0.0.1:8080/Packages.gz >/dev/null

Python 服务器只适合预览;长期服务请使用正常维护的 HTTP 服务器。

配置客户端

REPO_HOST 换成客户端能访问的地址。

# /etc/yum.repos.d/sow-quickstart.repo
[sow-quickstart]
name=SOW Quick Start
baseurl=http://REPO_HOST:8080/
enabled=1
gpgcheck=0
repo_gpgcheck=0
# /etc/apt/sources.list.d/sow-quickstart.list
deb [trusted=yes] http://REPO_HOST:8080/ ./

刷新索引并安装软件包:

sudo dnf makecache
sudo dnf install PACKAGE_NAME
sudo apt update
sudo apt install PACKAGE_NAME

APT source 末尾的 ./ 表示平面仓库。[trusted=yes] 与关闭 DNF 签名检查只适用于这个 未签名的快速示例;需要真实性保证时应使用已签名 Managed 仓库。

更新仓库

增删包文件后重新执行同一条命令:

sow create /srv/repo

目录内容就是 Plain 模式的全部状态。包字节不变时,生成的元数据具有确定性,重复运行会报告 noop=true

自动化场景可使用带版本的 JSON 信封:

sow create /srv/repo --json

何时使用 Managed 模式

如果目录已经恰好包含要发布的全部内容,使用 Plain。需要具名 Dist、架构视图、成员策略、 已签名元数据、Generation、审计或发布目标时,使用 Managed 工作区

另见 sow createPlain 平面仓库

3 - 第一个工作区

创建工作区,建立 RPM/DEB Dist,添加软件包并校验公共树。

Managed 模式会持久保存配置、成员关系、Generation 与审计状态。下面从空目录开始。

初始化工作区

sow init /srv/sow
cd /srv/sow

init 创建:

/srv/sow/
├── sow.yml   # 配置;schema: sow/v3
└── .sow/     # SQLite 状态、锁、staging、恢复与操作日志

不要编辑或对外服务 .sow/init 是幂等操作:重复执行会校验并收敛已声明的 Repository 与 Dist,不会重置有效工作区。

默认架构族是 x86_64aarch64。配置接受 amd64arm64 别名,并规范化为上述族名。

创建 Repository 与两个 Dist

sow repo new local
sow dist new el9 --format rpm
sow dist new bookworm --format deb

一个 Repository 拥有一棵公共 pool/ + dists/ 树和一份私有状态数据库。每个 Dist 只有 一种格式。dist new 会立即生成合法空视图,客户端读取空 Dist 时得到空索引而不是 404。

此时公共布局为:

/srv/sow/local/
├── pool/
└── dists/
    ├── el9/
    │   ├── x86_64/repodata/
    │   └── aarch64/repodata/
    └── bookworm/
        ├── Release
        └── main/
            ├── binary-amd64/{Packages,Packages.gz,by-hash/}
            └── binary-arm64/{Packages,Packages.gz,by-hash/}

添加软件包

显式选择目标 Dist:

sow add /path/to/packages/*.rpm -d el9
sow add /path/to/packages/*.deb -d bookworm

SOW 从包本身读取身份与架构,把接受的字节存入 local/pool/,更新 Desired Membership, 并在返回前构建受影响的 Dist。输入路径只用于导入;后续构建使用 Managed 包池。

需要合并多次成员变更时,用 --skip 暂不构建,最后统一收敛:

sow add /path/to/more/*.rpm -d el9 --skip
sow build

Desired Membership 领先于 Built Generation 时,Repository 状态为 dirty,且 ready_to_copy=false

查看与校验

sow status
sow ls -d el9
sow ls -d bookworm
sow check

status 是低成本状态读取。check 是交付门禁:它校验配置、状态、公共文件权限、保留根、 包字节、Desired Membership、索引、签名与 Generation manifest,且不写入任何内容。 只有 clean 且所有层都通过的 Repository 才返回成功。

查看规范化配置与默认值:

sow config show --all

对外服务 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 - 核心概念

SOW 模型:Plain 与 Managed、包池与视图、Desired Membership 与 Built Generation。

Plain 还是 Managed

两条运行路径相互独立。

Plain Managed
入口 sow create DIR initrepodistaddrmbuild
状态 软件包目录 sow.yml 加私有 SQLite/操作日志
公共布局 平面 RPM/DEB 索引 Repository pool/ + dists/
格式 RPM 与 DEB 可共存于一个目录 每个 Dist 一种格式
架构视图
策略与审计
元数据签名与发布目标

目录内容已经等于目标仓库时使用 Plain。需要由 SOW 管理成员、策略、Generation、签名、 审计或发布时使用 Managed。

Managed 层级

Workspace                    /srv/sow
├── sow.yml                  配置
├── .sow/                    私有状态;绝不对外服务
└── Repository               /srv/sow/local
    ├── pool/                规范包体
    └── dists/
        └── Dist             一个具名 RPM 或 DEB 成员集
            └── views        按架构渲染的元数据
  • Workspace 是配置与发现边界。
  • Repository 是隔离、Generation、发布和公共树边界。不同 Repository 之间不去重包体。
  • Dist 是单一包格式的具名成员集合。
  • 架构视图 是派生输出,不是第二套成员关系。noarch RPM 与 all DEB 会进入所有适用 视图,但包池字节不重复。

一条规范包体路径

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 -> Desired Membership (revision)
                    |
                  build
                    v
             Built Generation -> pool/ + dists/

addrm 默认构建受影响的 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 不应发布。

继续阅读