10d8800f07
Give ack, builder, and deployer an explicit init/check mode that reports missing project config instead of failing mid-work. Point builder at makefile.builder so its contract targets do not collide with an existing Makefile.
151 lines
7.3 KiB
Markdown
151 lines
7.3 KiB
Markdown
# Builder 构建发布契约 v1
|
||
|
||
本契约是 builder skill 的规范本体。`scripts/check.py` 是它的可执行形态:改契约先改
|
||
check.py,本文档跟随。所有接入项目按同一套 make 目标、产物形状和环境变量执行,
|
||
builder 脚本只做发布,不做项目特定的构建逻辑。
|
||
|
||
分工原则:**make 管构建(项目内、确定性),skill 脚本管发布(跨项目 SSOT),
|
||
Agent 只保留授权判断和歧义处理。**
|
||
|
||
契约文件固定为项目根 `makefile.builder`,调用方式:
|
||
|
||
```bash
|
||
make -f makefile.builder <target>
|
||
```
|
||
|
||
不要把 builder 目标写进用户的 `Makefile` 或 `makefile`。`check.py` 只读
|
||
`makefile.builder`。
|
||
|
||
## 1. Make 目标
|
||
|
||
### 必备目标(所有项目)
|
||
|
||
| 目标 | 要求 |
|
||
|------|------|
|
||
| `help` | 分组列出全部目标;首屏含当前版本 |
|
||
| `version` | 输出一行版本号,适合脚本消费 |
|
||
| `clean` | 只删除明确、受限的构建产物目录 |
|
||
| `build` | 编译/打包主产物;尊重 `ARCH`;**不得内含任何上传动作** |
|
||
|
||
### 条件目标
|
||
|
||
| 目标 | 适用 | 要求 |
|
||
|------|------|------|
|
||
| `deb` | 有 DEB 产物的项目 | 产出唯一 `$(DIST_DIR)/<name>_<version>_<arch>.deb`;只构建不上传 |
|
||
| `docker` | 有镜像的项目 | 构建本地单平台镜像 `linux/$(ARCH)`;**禁止 `--push`、禁止多平台** |
|
||
| `push-deb` | 同时有 DEB 和镜像的项目 | 仅调 builder 的 `upload_deb.sh` 上传 `dist/*.deb` |
|
||
| `push-docker` | 同时有 DEB 和镜像的项目 | 仅调 builder 的 `publish_docker.sh` |
|
||
| `push` | 单一产物类型时必备;双产物项目为聚合 | 依序调用对应 push-* 或直接调脚本;是发布的唯一 make 入口 |
|
||
|
||
规则:
|
||
|
||
1. 项目有 DEB 产物的判据:`makefile.builder` 配方引用 `dpkg-deb`/`debuild` 或产出 `.deb`。
|
||
有镜像的判据:项目根存在 `Dockerfile`。
|
||
2. 双产物项目必须拆 `push-deb`/`push-docker`,`push` 依序聚合两者;单产物项目一个
|
||
`push` 即可。
|
||
3. `docker` 目标只能本地构建。多平台镜像无法拆成"make 构建 + 单独推送"
|
||
(`buildx --push` 是一步),因此多平台发布只能走 `publish_docker.sh`。
|
||
4. push 类目标必须是薄包装:解析脚本路径后委托,不内联 curl/token/端点。
|
||
|
||
## 2. 变量
|
||
|
||
| 变量 | 默认 | 说明 |
|
||
|------|------|------|
|
||
| `ARCH` | `amd64` | 仅允许 `amd64` \| `arm64`,非法值必须 `$(error)` 报错并提示合法值 |
|
||
| `VERSION` | `` (空) | 为空时按「版本号」节从 Git 祖先稳定 tag 推导;禁止 `sort -V` 取全局最新 |
|
||
| `DIST_DIR` | `dist` | DEB 产物目录 |
|
||
| `PROJECT_NAME` | git 仓库名 | 包名/镜像名主体 |
|
||
|
||
### 版本号
|
||
|
||
`make -f makefile.builder version` 输出一行规范版本(无 `v` 前缀)。DEB 的
|
||
`Version` 与文件名直接用它;Docker tag 由它渲染。推导入口是 `scripts/version.sh`
|
||
(`makefile.builder` 通过 `scripts/version.mk` 引用);Builder 只读取 Git 状态,不调用
|
||
manage-release,不猜测下一个正式 SemVer。正式 tag 由 manage-release 事先打好。
|
||
`version.sh` 不执行 `git fetch`。
|
||
|
||
推导前 `git fetch --tags`(本地 linked worktree 共享 tags,不必再 fetch 才
|
||
能看见其它 worktree 新打的 tag)。基线是 **HEAD 祖先上最近的稳定 tag**,
|
||
不是全仓库 `sort -V` 的最大号。稳定 tag 仅 `v<major>.<minor>.<patch>`。
|
||
|
||
| 判定 | 规范版本 | DEB 文件 | Docker tag |
|
||
| --- | --- | --- | --- |
|
||
| HEAD exact-match 稳定 tag `v1.4.2` | `1.4.2` | `name_1.4.2_<arch>.deb` | `1.4.2` |
|
||
| 其它 commit;祖先最近稳定 tag `v1.4.2` | `1.4.2~<branch>.<n>+g<sha>` | `name_1.4.2~<branch>.<n>+g<sha>_<arch>.deb` | `1.4.2-<branch>.<n>.g<sha>` |
|
||
| 祖先中没有稳定 tag | `0.0.0~<branch>+g<sha>` | 同上替换规范版本 | 同上映射 |
|
||
|
||
测试与正式进入同一 apt / Docker 仓库。测试 DEB 必须用 `~`,使
|
||
`1.4.2~…` < `1.4.2`,`apt upgrade` 不会装上测试包。Docker tag 不得含
|
||
`~`、`/`、`:`,由规范版本把 `~` → `-`、`+g` → `.g`。
|
||
|
||
`<branch>`:`git rev-parse --abbrev-ref HEAD`,detached 时用
|
||
`BUILD_BRANCH` / `CI_COMMIT_BRANCH` / `GITHUB_REF_NAME`,再没有则
|
||
`detached`。清洗:小写;`/`、`_` → `-`;去掉非 `[a-z0-9-]`;压缩连续
|
||
`-`;过长截断(给 base、距离、SHA 留位置;Docker tag 上限 128)。
|
||
`<n>` 为基线 tag 到 HEAD 的 commit 数;`<sha>` 为 7 位短哈希。不要把
|
||
`--dirty` 写入版本;脏树发布仍走既有门禁。显式 `VERSION=` / `IMAGE_TAG=`
|
||
可覆盖推导,但不得把非 exact-match 的 commit 标成正式 `X.Y.Z`。
|
||
|
||
`makefile.builder` 不要内联 `git describe` 或 `sort -V`,include 本 skill 的
|
||
`scripts/version.mk`:
|
||
|
||
```makefile
|
||
include $(HOME)/.pouch/skills/builder/scripts/version.mk
|
||
```
|
||
|
||
## 3. 发布环境变量
|
||
|
||
### DEB 轨道
|
||
|
||
| 变量 | 必填 | 说明 |
|
||
|------|------|------|
|
||
| `DEB_SERVER_URL` | 是 | 仓库服务地址 |
|
||
| `DEB_TOKEN` | 是 | 认证令牌;只从环境读取,绝不进 argv/日志/git |
|
||
| `DEB_REPOSITORY` | 是 | 目标仓库名 |
|
||
| `DEB_UPLOAD_PATH` | 否 | 覆盖默认上传路径 `/api/v2/upload/package` |
|
||
|
||
### Docker 轨道
|
||
|
||
| 变量 | 必填 | 说明 |
|
||
|------|------|------|
|
||
| `DOCKER_REGISTRY` | 是 | registry 主机,无 scheme |
|
||
| `DOCKER_REPOSITORY` | 否 | 默认取 git 仓库名 |
|
||
| `IMAGE_TAG` | 否 | 默认由规范版本渲染:正式为 `X.Y.Z`;测试将 `~` 换成 `-`、`+g` 换成 `.g` |
|
||
| `PLATFORMS` | 否 | 默认 `linux/amd64`;多平台如 `linux/amd64,linux/arm64` |
|
||
|
||
配置来源优先级:shell 已显式设置的值 > 项目根 `.env` > 失败并询问用户。
|
||
`.env` 由 builder 脚本自动向上查找并加载(不回显任何值);当前 shell 已设置的值
|
||
优先于 `.env`。
|
||
|
||
### 工作区安全
|
||
|
||
脏工作树(有未提交修改)默认拒绝发布;`ALLOW_UNCOMMITTED=1` 显式放行并在汇报中
|
||
注明镜像/包包含哪些未提交修改。该门在 builder 脚本层实现,不在 make 层。
|
||
|
||
## 4. 脚本解析顺序
|
||
|
||
push 目标与 `version.sh` 定位 builder 脚本时按以下顺序,命中即用,不做静默兜底:
|
||
|
||
1. `$BUILDER_SKILL_DIR/scripts/`(特殊安装位置)
|
||
2. `$HOME/.pouch/skills/builder/scripts/`(标准 clone 位)
|
||
|
||
两个位置都不可用时必须失败并提示:设置 `BUILDER_SKILL_DIR`,或把 pouch 仓库
|
||
clone 到 `~/.pouch`。
|
||
|
||
## 5. 校验
|
||
|
||
`python3 -I -S <builder-scripts>/check.py <project-dir> [--build]` 对本项目逐条检查
|
||
上述要求,任一 FAIL 退出码非零,可直接挂 CI。`--build` 额外实构
|
||
`make -f makefile.builder deb` 并核对产物元数据(默认只静态检查配方)。`--ready`
|
||
额外检查轨道工具链,以及 `.env` / 环境中的发布键名是否存在(不读取、不打印值;
|
||
缺键只挡住发布)。校验失败时的修复路径:按 `templates/makefile.builder` 补齐或
|
||
修正 `makefile.builder`,再跑 check.py,不要绕过校验器,不要改用户 Makefile,
|
||
不要用 create-makefile(版本推导与本契约冲突)。
|
||
|
||
## 6. 存量项目(legacy fallback)
|
||
|
||
未接入契约的项目:builder 仍可按发现流程工作——从用户 `Makefile`、CI 配置、`debian/`
|
||
与发布文档中找已有构建/上传入口,优先复用;上传仍使用 builder 脚本。完成一次成功
|
||
交付后应引导用户按 `templates/makefile.builder` 写入项目根 `makefile.builder`,之后以
|
||
check.py 为准。不把契约目标合并进用户 Makefile。
|